From 8e77a64c5be5a0b7b19c53549d91b0c45c37df3a Mon Sep 17 00:00:00 2001 From: Scott Heath Date: Tue, 8 Dec 2020 15:09:40 -0800 Subject: [PATCH] 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) --- ACL/include/ACL/Transport/MessageRouter.h | 31 +- ACL/src/Transport/MessageRouter.cpp | 53 +- .../Transport/PostConnectSequencerFactory.cpp | 2 +- ACL/test/Transport/MessageRouterTest.cpp | 62 +- ACL/test/Transport/MessageRouterTest.h | 14 +- ACL/test/Transport/MockHTTP2Request.h | 2 +- .../Transport/MockMessageRouterObserver.h | 58 +- ADSL/src/DirectiveSequencer.cpp | 2 +- .../AVS/Attachment/AttachmentWriter.h | 32 + .../AVSCommon/AVS/ComponentConfiguration.h | 4 +- .../AVS/src/AbstractAVSConnectionManager.cpp | 2 - AVSCommon/AVS/src/ComponentConfiguration.cpp | 8 +- AVSCommon/AVS/test/CapabilityAgentTest.cpp | 12 +- .../test/ExceptionEncounteredSenderTest.cpp | 4 +- AVSCommon/CMakeLists.txt | 2 + .../SDKInterfaces/ChannelVolumeInterface.h | 42 ++ ...PContentFetcherInterfaceFactoryInterface.h | 3 + .../PowerResourceManagerInterface.h | 15 + .../MockChannelVolumeInterface.h | 20 +- .../Utils/Configuration/ConfigurationNode.h | 11 + .../AVSCommon/Utils/ID3Tags/ID3v2Tags.h | 47 ++ .../LibcurlUtils/CurlEasyHandleWrapper.h | 53 +- .../DefaultSetCurlOptionsCallbackFactory.h | 50 ++ .../LibcurlUtils/HTTPContentFetcherFactory.h | 16 +- .../LibcurlUtils/LibCurlHttpContentFetcher.h | 16 +- .../LibcurlUtils/LibcurlHTTP2Connection.h | 17 +- .../LibcurlHTTP2ConnectionFactory.h | 21 +- .../Utils/LibcurlUtils/LibcurlHTTP2Request.h | 11 +- ...rlSetCurlOptionsCallbackFactoryInterface.h | 51 ++ .../LibcurlSetCurlOptionsCallbackInterface.h | 53 ++ .../AVSCommon/Utils/MediaPlayer/ErrorTypes.h | 7 +- .../Utils/MediaPlayer/PlaybackContext.h | 4 +- .../AVSCommon/Utils/PlatformDefinitions.h | 4 + .../include/AVSCommon/Utils/SDKVersion.h | 4 +- .../Utils/Timing/SystemClockMonitor.h | 6 + .../src/Configuration/ConfigurationNode.cpp | 4 + AVSCommon/Utils/src/ID3Tags/ID3v2Tags.cpp | 92 +++ .../LibcurlUtils/CurlEasyHandleWrapper.cpp | 7 +- .../DefaultSetCurlOptionsCallbackFactory.cpp | 31 + .../HTTPContentFetcherFactory.cpp | 17 +- AVSCommon/Utils/src/LibcurlUtils/HttpPut.cpp | 2 +- .../LibCurlHttpContentFetcher.cpp | 42 +- .../LibcurlUtils/LibcurlHTTP2Connection.cpp | 12 +- .../LibcurlHTTP2ConnectionFactory.cpp | 18 +- .../src/LibcurlUtils/LibcurlHTTP2Request.cpp | 15 +- .../Utils/src/MediaPlayer/PlaybackContext.cpp | 41 +- .../Utils/src/Metrics/MetricEventBuilder.cpp | 5 +- .../Power/AggregatedPowerResourceManager.cpp | 3 +- AVSCommon/Utils/src/RequiresShutdown.cpp | 6 +- .../Utils/src/Stream/StreamFunctions.cpp | 2 +- AVSCommon/Utils/src/SystemClockMonitor.cpp | 4 + AVSCommon/Utils/src/UUIDGeneration.cpp | 10 +- .../Utils/MediaPlayer/PlaybackContextTest.cpp | 50 +- AVSCommon/Utils/test/ID3v2TagsTest.cpp | 94 +++ .../include/DefaultClient/DefaultClient.h | 46 +- .../DefaultClient/DefaultClientComponent.h | 20 +- .../DefaultClient/src/CMakeLists.txt | 4 +- .../DefaultClient/src/DefaultClient.cpp | 220 ++----- .../src/DefaultClientComponent.cpp | 49 +- .../Audio/include/Audio/AudioFactory.h | 10 +- .../Resources/Audio/src/AudioFactory.cpp | 4 + .../BlueZ/src/GVariantTupleReader.cpp | 3 +- CHANGELOG.md | 5 +- CMakeLists.txt | 2 +- .../SQLiteCapabilitiesDelegateStorage.h | 14 + .../Utils/DiscoveryUtils.h | 13 + .../src/CapabilitiesDelegate.cpp | 5 +- .../src/DiscoveryEventSender.cpp | 2 +- .../SQLiteCapabilitiesDelegateStorage.cpp | 37 +- .../src/Utils/DiscoveryUtils.cpp | 58 +- .../SQLiteCapabilitiesDelegateStorageTest.cpp | 50 +- .../test/Utils/DiscoveryUtilsTest.cpp | 135 +++-- .../AIP/include/AIP/AudioInputProcessor.h | 5 + .../AIP/src/AudioInputProcessor.cpp | 49 +- .../src/RangeControllerCapabilityAgent.cpp | 1 - .../SpeakerManager/ChannelVolumeManager.h | 1 + .../include/SpeakerManager/SpeakerManager.h | 61 +- .../src/ChannelVolumeManager.cpp | 4 + .../SpeakerManager/src/SpeakerManager.cpp | 173 +++--- .../test/SpeakerManagerTest.cpp | 20 +- .../System/src/SoftwareInfoSendRequest.cpp | 27 +- .../System/src/SystemCapabilityProvider.cpp | 2 +- .../System/test/LocaleHandlerTest.cpp | 4 +- .../System/test/StateReportGeneratorTest.cpp | 8 +- .../Implementation/src/SystemClockDelay.cpp | 4 +- Endpoints/src/EndpointBuilder.cpp | 4 - Integration/test/AlertsIntegrationTest.cpp | 4 +- .../AudioInputProcessorIntegrationTest.cpp | 6 +- Integration/test/CMakeLists.txt | 2 +- ...onTests.cpp => NetworkIntegrationTest.cpp} | 0 KWD/KittAi/src/KittAiKeyWordDetector.cpp | 3 +- KWD/Sensory/src/SensoryKeywordDetector.cpp | 3 +- .../include/PlaylistParser/ContentDecrypter.h | 5 +- .../include/PlaylistParser/Id3TagsRemover.h | 113 ++++ .../UrlContentToAttachmentConverter.h | 4 + PlaylistParser/src/CMakeLists.txt | 1 + PlaylistParser/src/ContentDecrypter.cpp | 11 +- PlaylistParser/src/Id3TagsRemover.cpp | 244 ++++++++ .../src/UrlContentToAttachmentConverter.cpp | 29 +- PlaylistParser/test/ContentDecrypterTest.cpp | 19 +- PlaylistParser/test/Id3TagsRemoverTest.cpp | 348 +++++++++++ .../test/PlaylistParser/MockContentFetcher.h | 4 +- PlaylistParser/test/PlaylistParserTest.cpp | 6 +- .../test/CustomerDataManagerTest.cpp | 3 +- .../test/RegistrationManagerTest.cpp | 2 +- SampleApp/src/ConsoleReader.cpp | 2 +- .../DefaultEndpointModeControllerHandler.cpp | 15 +- .../DefaultEndpointRangeControllerHandler.cpp | 23 +- SampleApp/src/ExternalCapabilitiesBuilder.cpp | 2 +- ...eripheralEndpointModeControllerHandler.cpp | 15 +- ...ripheralEndpointPowerControllerHandler.cpp | 15 +- ...ripheralEndpointRangeControllerHandler.cpp | 14 +- SampleApp/src/SampleApplication.cpp | 11 +- .../include/Settings/DeviceSettingsManager.h | 13 +- Settings/include/Settings/SettingsManager.h | 69 +++ .../Settings/SettingsManagerBuilderBase.h | 13 - .../Storage/SQLiteDeviceSettingStorage.h | 11 + .../Storage/SQLiteDeviceSettingStorage.cpp | 16 + Settings/src/Types/NetworkInfo.cpp | 15 +- Settings/test/SettingCallbackAdapterTest.cpp | 8 +- Settings/test/SettingCallbacksTest.cpp | 8 +- Settings/test/Settings/MockSetting.h | 2 +- Settings/test/SettingsManagerTest.cpp | 10 +- SpeechEncoder/src/SpeechEncoder.cpp | 2 +- .../include/SQLiteStorage/SQLiteMiscStorage.h | 238 +++++++- .../SQLiteStorage/src/SQLiteMiscStorage.cpp | 192 ++++-- .../test/SQLiteMiscStorageTest.cpp | 80 ++- .../PostConnectSynchronizeStateSender.h | 12 +- .../SynchronizeStateSenderFactory.h | 16 +- .../src/PostConnectSynchronizeStateSender.cpp | 67 ++- .../src/SynchronizeStateSenderFactory.cpp | 21 +- SynchronizeStateSender/test/CMakeLists.txt | 2 +- .../PostConnectSynchronizeStateSenderTest.cpp | 12 + applications/CMakeLists.txt | 2 + .../CMakeLists.txt | 4 + .../DeviceSettingsManagerBuilder.h | 33 +- .../DeviceSettingsManagerComponent.h | 59 ++ .../src/CMakeLists.txt | 20 + .../src/DeviceSettingsManagerBuilder.cpp | 49 +- .../src/DeviceSettingsManagerComponent.cpp | 32 + .../AlexaCommunicationsComponent.h | 5 + .../src/AlexaCommunicationsComponent.cpp | 21 +- .../HTTPContentFetcherComponent.h | 8 +- .../src/HTTPContentFetcherComponent.cpp | 23 +- .../acsdkNullSystemTimeZone/CMakeLists.txt | 4 + .../SystemTimeZoneComponent.h | 43 ++ .../src/CMakeLists.txt | 12 + .../src/SystemTimeZoneComponent.cpp | 31 + .../PreviewAlexaClient.h | 3 - .../PreviewAlexaClientComponent.h | 9 +- .../src/CMakeLists.txt | 6 +- .../src/PreviewAlexaClient.cpp | 55 +- .../src/PreviewAlexaClientComponent.cpp | 57 +- build/cmake/DefaultLibNames.cmake | 2 + .../include/acsdkAlerts/AlertScheduler.h | 8 +- .../acsdkAlerts/AlertsCapabilityAgent.h | 91 ++- .../include/acsdkAlerts/AlertsComponent.h | 84 +++ .../include/acsdkAlerts/Renderer/Renderer.h | 30 +- .../Storage/AlertStorageInterface.h | 6 +- .../acsdkAlerts/Storage/SQLiteAlertStorage.h | 20 +- .../Alerts/acsdkAlerts/src/AlertScheduler.cpp | 12 +- .../acsdkAlerts/src/AlertsCapabilityAgent.cpp | 119 +++- .../acsdkAlerts/src/AlertsComponent.cpp | 103 ++++ .../Alerts/acsdkAlerts/src/CMakeLists.txt | 3 + .../acsdkAlerts/src/Renderer/Renderer.cpp | 37 ++ .../src/Storage/SQLiteAlertStorage.cpp | 39 +- .../acsdkAlerts/test/AlertSchedulerTest.cpp | 8 +- .../test/AlertsCapabilityAgentTest.cpp | 11 +- .../Alerts/acsdkAlerts/test/CMakeLists.txt | 4 + .../test/Renderer/RendererTest.cpp | 39 +- .../include/acsdkAudioPlayer/AudioPlayer.h | 105 +++- .../acsdkAudioPlayer/src/AudioPlayer.cpp | 546 ++++++++++++------ .../acsdkAudioPlayer/test/AudioPlayerTest.cpp | 22 +- .../acsdkBluetooth/SQLiteBluetoothStorage.h | 5 + .../acsdkBluetooth/src/Bluetooth.cpp | 7 +- .../src/SQLiteBluetoothStorage.cpp | 55 +- .../test/SQLiteBluetoothStorageTest.cpp | 18 + .../DoNotDisturbCapabilityAgent.h | 102 +++- .../acsdkDoNotDisturb/DoNotDisturbComponent.h | 57 ++ .../acsdkDoNotDisturb/src/CMakeLists.txt | 5 +- .../src/DoNotDisturbCapabilityAgent.cpp | 63 +- .../src/DoNotDisturbComponent.cpp | 31 + .../test/ExternalMediaPlayerTest.cpp | 1 + .../ExternalMediaAdapterConstants.h | 1 + .../ExternalMediaAdapterInterface.h | 7 + .../src/AdapterUtils.cpp | 1 + .../acsdkMultiRoomMusic/src/CMakeLists.txt | 2 +- .../SQLiteNotificationsStorage.h | 14 + .../src/NotificationsCapabilityAgent.cpp | 6 +- .../src/SQLiteNotificationsStorage.cpp | 49 +- .../acsdkManufactory/internal/TypeIndex.h | 1 + .../include/acsdkShared/SharedComponent.h | 2 + shared/acsdkShared/src/SharedComponent.cpp | 3 +- .../src/ShutdownManager.cpp | 2 +- .../src/StartupManager.cpp | 39 +- tools/Install/pi.sh | 23 +- tools/Install/setup.sh | 5 +- 197 files changed, 5055 insertions(+), 1154 deletions(-) create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/ID3Tags/ID3v2Tags.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/DefaultSetCurlOptionsCallbackFactory.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlSetCurlOptionsCallbackFactoryInterface.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlSetCurlOptionsCallbackInterface.h create mode 100644 AVSCommon/Utils/src/ID3Tags/ID3v2Tags.cpp create mode 100644 AVSCommon/Utils/src/LibcurlUtils/DefaultSetCurlOptionsCallbackFactory.cpp create mode 100644 AVSCommon/Utils/test/ID3v2TagsTest.cpp rename Integration/test/{NetworkIntegrationTests.cpp => NetworkIntegrationTest.cpp} (100%) create mode 100644 PlaylistParser/include/PlaylistParser/Id3TagsRemover.h create mode 100644 PlaylistParser/src/Id3TagsRemover.cpp create mode 100644 PlaylistParser/test/Id3TagsRemoverTest.cpp create mode 100644 applications/acsdkDefaultDeviceSettingsManager/CMakeLists.txt rename {ApplicationUtilities/DefaultClient/include/DefaultClient => applications/acsdkDefaultDeviceSettingsManager/include/acsdkDeviceSettingsManager}/DeviceSettingsManagerBuilder.h (79%) create mode 100644 applications/acsdkDefaultDeviceSettingsManager/include/acsdkDeviceSettingsManager/DeviceSettingsManagerComponent.h create mode 100644 applications/acsdkDefaultDeviceSettingsManager/src/CMakeLists.txt rename {ApplicationUtilities/DefaultClient => applications/acsdkDefaultDeviceSettingsManager}/src/DeviceSettingsManagerBuilder.cpp (80%) create mode 100644 applications/acsdkDefaultDeviceSettingsManager/src/DeviceSettingsManagerComponent.cpp create mode 100644 applications/acsdkNullSystemTimeZone/CMakeLists.txt create mode 100644 applications/acsdkNullSystemTimeZone/include/acsdkSystemTimeZone/SystemTimeZoneComponent.h create mode 100644 applications/acsdkNullSystemTimeZone/src/CMakeLists.txt create mode 100644 applications/acsdkNullSystemTimeZone/src/SystemTimeZoneComponent.cpp create mode 100644 capabilities/Alerts/acsdkAlerts/include/acsdkAlerts/AlertsComponent.h create mode 100644 capabilities/Alerts/acsdkAlerts/src/AlertsComponent.cpp create mode 100644 capabilities/DoNotDisturb/acsdkDoNotDisturb/include/acsdkDoNotDisturb/DoNotDisturbComponent.h create mode 100644 capabilities/DoNotDisturb/acsdkDoNotDisturb/src/DoNotDisturbComponent.cpp diff --git a/ACL/include/ACL/Transport/MessageRouter.h b/ACL/include/ACL/Transport/MessageRouter.h index e35ad68907..5618d33514 100644 --- a/ACL/include/ACL/Transport/MessageRouter.h +++ b/ACL/include/ACL/Transport/MessageRouter.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "ACL/Transport/MessageConsumerInterface.h" #include "ACL/Transport/MessageRouterInterface.h" @@ -49,6 +50,9 @@ class MessageRouter , public MessageConsumerInterface , public std::enable_shared_from_this { public: + /// Amount of time to allow for an automatic reconnect before notifying of a server side disconnect. + static const std::chrono::milliseconds DEFAULT_SERVER_SIDE_DISCONNECT_GRACE_PERIOD; + /** * Factory function for creating an instance of MessageRouterInterface. * @@ -77,13 +81,16 @@ class MessageRouter * is used. * @param engineType optional parameter of engine type associated with this MessageRouter. Default to be * ENGINE_TYPE_ALEXA_VOICE_SERVICES. + * @param serverSideDisconnectGracePeriod How long to allow for an automatic reconnection before reporting + * a server side disconnect to our observer. */ MessageRouter( std::shared_ptr authDelegate, std::shared_ptr attachmentManager, std::shared_ptr transportFactory, const std::string& avsGateway = "", - int engineType = avsCommon::sdkInterfaces::ENGINE_TYPE_ALEXA_VOICE_SERVICES); + int engineType = avsCommon::sdkInterfaces::ENGINE_TYPE_ALEXA_VOICE_SERVICES, + std::chrono::milliseconds serverSideDisconnectGracePeriod = DEFAULT_SERVER_SIDE_DISCONNECT_GRACE_PERIOD); /// @name MessageRouterInterface methods. /// @{ @@ -136,6 +143,16 @@ class MessageRouter const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status status, const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::ChangedReason reason); + /** + * Without any further queueing to the executor, notify the connection observer that the status has changed. + * + * @param status The current status of the connection. + * @param reason The reason the connection status changed. + */ + void handleNotifyObserverOnConnectionStatusChanged( + const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status status, + const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::ChangedReason reason); + /** * Notify the message observer of an incoming message from AVS. * Architectural note: @@ -228,6 +245,18 @@ class MessageRouter /// The synchonized queue of messages to send that is shared between transports. std::shared_ptr m_requestQueue; + /// Timer used to smooth over server side disconnects. + avsCommon::utils::timing::Timer m_serverSideDisconnectTimer; + + /// True if notification of a server side disconnect should be delivered when the timer triggers. + bool m_serverSideDisconnectNotificationPending; + + /// The last connection status reported to our observer. + avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status m_lastReportedConnectionStatus; + + /// Amount of time to allow for an automatic reconnect before notifying of a server side disconnect. + const std::chrono::milliseconds m_serverSideReconnectGracePeriod; + protected: /** * Executor to perform asynchronous operations: diff --git a/ACL/src/Transport/MessageRouter.cpp b/ACL/src/Transport/MessageRouter.cpp index a52f7e3392..49819897d1 100644 --- a/ACL/src/Transport/MessageRouter.cpp +++ b/ACL/src/Transport/MessageRouter.cpp @@ -36,6 +36,8 @@ static const std::string TAG("MessageRouter"); /// String for logging purpose as the key for the size of m_transports. static constexpr const char* KEY_SIZEOF_TRANSPORTS = "sizeOf m_transports"; +const std::chrono::milliseconds MessageRouter::DEFAULT_SERVER_SIDE_DISCONNECT_GRACE_PERIOD(15000); + /** * Create a LogEntry using this file's TAG and the specified event string. * @@ -74,7 +76,8 @@ MessageRouter::MessageRouter( std::shared_ptr attachmentManager, std::shared_ptr transportFactory, const std::string& avsGateway, - int engineType) : + int engineType, + std::chrono::milliseconds serverSideDisconnectGracePeriod) : MessageRouterInterface{"MessageRouter"}, m_avsGateway{avsGateway}, m_authDelegate{authDelegate}, @@ -84,7 +87,10 @@ MessageRouter::MessageRouter( m_isEnabled{false}, m_attachmentManager{attachmentManager}, m_transportFactory{transportFactory}, - m_requestQueue{std::make_shared()} { + m_requestQueue{std::make_shared()}, + m_serverSideDisconnectNotificationPending{false}, + m_lastReportedConnectionStatus{ConnectionStatusObserverInterface::Status::DISCONNECTED}, + m_serverSideReconnectGracePeriod{serverSideDisconnectGracePeriod} { } MessageRouterInterface::ConnectionStatus MessageRouter::getConnectionStatus() { @@ -258,10 +264,8 @@ void MessageRouter::onDisconnected( void MessageRouter::onServerSideDisconnect(std::shared_ptr transport) { std::unique_lock lock{m_connectionMutex}; - ACSDK_INFO(LX("server-side disconnect received") - .d("Message router is enabled", m_isEnabled) - .p("transport", transport) - .p("m_activeTransport", m_activeTransport)); + ACSDK_INFO( + LX(__func__).d("m_isEnabled", m_isEnabled).p("transport", transport).p("m_activeTransport", m_activeTransport)); if (m_isEnabled && transport == m_activeTransport) { setConnectionStatusLocked( ConnectionStatusObserverInterface::Status::PENDING, @@ -299,14 +303,47 @@ void MessageRouter::notifyObserverOnConnectionStatusChanged( const ConnectionStatusObserverInterface::Status status, const ConnectionStatusObserverInterface::ChangedReason reason) { auto task = [this, status, reason]() { + if (m_serverSideReconnectGracePeriod != std::chrono::milliseconds(0) && + ConnectionStatusObserverInterface::Status::PENDING == status && + ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT == reason) { + m_serverSideDisconnectNotificationPending = true; + m_serverSideDisconnectTimer.start(m_serverSideReconnectGracePeriod, [this]() { + ACSDK_DEBUG0(LX("serverSideDisconectTimerPredicate")); + m_executor.submit([this]() { + ACSDK_DEBUG0( + LX("serverSideDisconectTimerHandler") + .d("m_serverSideDisconnectNotificationPending", m_serverSideDisconnectNotificationPending)); + if (m_serverSideDisconnectNotificationPending) { + handleNotifyObserverOnConnectionStatusChanged( + ConnectionStatusObserverInterface::Status::PENDING, + ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT); + } + }); + }); + } else { + if (m_serverSideDisconnectNotificationPending) { + ACSDK_DEBUG0(LX("NotificationOfServerSideDisconnectSuppressed")); + } + m_serverSideDisconnectTimer.stop(); + handleNotifyObserverOnConnectionStatusChanged(status, reason); + } + }; + m_executor.submit(task); +} + +void MessageRouter::handleNotifyObserverOnConnectionStatusChanged( + const ConnectionStatusObserverInterface::Status status, + const ConnectionStatusObserverInterface::ChangedReason reason) { + m_serverSideDisconnectNotificationPending = false; + if (status != m_lastReportedConnectionStatus) { + m_lastReportedConnectionStatus = status; auto observer = getObserver(); if (observer) { std::vector statuses; statuses.emplace_back(m_engineType, reason, status); observer->onConnectionStatusChanged(status, statuses); } - }; - m_executor.submit(task); + } } void MessageRouter::notifyObserverOnReceive(const std::string& contextId, const std::string& message) { diff --git a/ACL/src/Transport/PostConnectSequencerFactory.cpp b/ACL/src/Transport/PostConnectSequencerFactory.cpp index 46506840d6..0a804eaec1 100644 --- a/ACL/src/Transport/PostConnectSequencerFactory.cpp +++ b/ACL/src/Transport/PostConnectSequencerFactory.cpp @@ -48,7 +48,7 @@ class LegacyProviderRegistrar : public PostConnectOperationProviderRegistrarInte * * @param postConnectOperationProviders The providers to (pre) register. */ - LegacyProviderRegistrar( + explicit LegacyProviderRegistrar( const std::vector>& postConnectOperationProviders); /// @name PostConnectOperationProviderRegistrarInterface methods. diff --git a/ACL/test/Transport/MessageRouterTest.cpp b/ACL/test/Transport/MessageRouterTest.cpp index 8dc8b67a1d..1675422123 100644 --- a/ACL/test/Transport/MessageRouterTest.cpp +++ b/ACL/test/Transport/MessageRouterTest.cpp @@ -158,7 +158,7 @@ TEST_F(MessageRouterTest, test_disconnectDisconnectsConnectedTransports) { m_router->disable(); } -TEST_F(MessageRouterTest, test_serverSideDisconnectCreatesANewTransport) { +TEST_F(MessageRouterTest, test_serverSideDisconnectWithLongDelayedReconnectReportsPending) { /* * This test is difficult to setup in a nice way. The idea is to replace the original * transport with a new one, call onServerSideDisconnect to make it the new active @@ -173,16 +173,70 @@ TEST_F(MessageRouterTest, test_serverSideDisconnectCreatesANewTransport) { m_transportFactory->setMockTransport(newTransport); - // Reset the MessageRouterObserver, there should be no interactions with the observer + // Trigger server side disconnect handling m_router->onServerSideDisconnect(oldTransport); + // Simulate delayed reconnect, waiting for the server side disconnect grace period to expire so + // so that we can see the transition back to the PENDING state. + ASSERT_TRUE(m_mockMessageRouterObserver->waitForStatusChange( + TestableMessageRouter::SHORT_SERVER_SIDE_DISCONNECT_GRACE_PERIOD + SHORT_TIMEOUT_MS, + ConnectionStatusObserverInterface::Status::PENDING, + ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT)) + << "status=" << m_mockMessageRouterObserver->getLatestConnectionStatus() + << "reason=" << m_mockMessageRouterObserver->getLatestConnectionChangedReason(); + + // mock the new transports connection + connectMockTransport(newTransport.get()); + m_router->onConnected(newTransport); + waitOnMessageRouter(SHORT_TIMEOUT_MS); ASSERT_EQ( - m_mockMessageRouterObserver->getLatestConnectionStatus(), ConnectionStatusObserverInterface::Status::PENDING); + m_mockMessageRouterObserver->getLatestConnectionStatus(), ConnectionStatusObserverInterface::Status::CONNECTED); ASSERT_EQ( m_mockMessageRouterObserver->getLatestConnectionChangedReason(), - ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT); + ConnectionStatusObserverInterface::ChangedReason::ACL_CLIENT_REQUEST); + + // mock the old transport disconnecting completely + disconnectMockTransport(oldTransport.get()); + m_router->onDisconnected(oldTransport, ConnectionStatusObserverInterface::ChangedReason::ACL_CLIENT_REQUEST); + + auto messageRequest = createMessageRequest(); + + EXPECT_CALL(*oldTransport.get(), onRequestEnqueued()).Times(0); + + EXPECT_CALL(*newTransport.get(), onRequestEnqueued()).Times(1); + + m_router->sendMessage(messageRequest); + + waitOnMessageRouter(SHORT_TIMEOUT_MS); +} + +TEST_F(MessageRouterTest, test_serverSideDisconnectWithReconnectDoesNotReportingPending) { + /* + * This test is difficult to setup in a nice way. The idea is to replace the original + * transport with a new one, call onServerSideDisconnect to make it the new active + * transport, and then send a message. The message should be sent on the new transport. + */ + setupStateToConnected(); + + auto oldTransport = m_mockTransport; + + auto newTransport = std::make_shared>(); + initializeMockTransport(newTransport.get()); + + m_transportFactory->setMockTransport(newTransport); + + // Trigger server side disconnect handling + m_router->onServerSideDisconnect(oldTransport); + + // Simulate slightly delayed reconnect, verifying that no transition to PENDING is reported. + ASSERT_FALSE(m_mockMessageRouterObserver->waitForStatusChange( + SHORT_TIMEOUT_MS, + ConnectionStatusObserverInterface::Status::PENDING, + ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT)) + << "status=" << m_mockMessageRouterObserver->getLatestConnectionStatus() + << "reason=" << m_mockMessageRouterObserver->getLatestConnectionChangedReason(); // mock the new transports connection connectMockTransport(newTransport.get()); diff --git a/ACL/test/Transport/MessageRouterTest.h b/ACL/test/Transport/MessageRouterTest.h index 6a1cfd5168..6478e24111 100644 --- a/ACL/test/Transport/MessageRouterTest.h +++ b/ACL/test/Transport/MessageRouterTest.h @@ -50,7 +50,13 @@ class TestableMessageRouter : public MessageRouter { std::shared_ptr attachmentManager, std::shared_ptr factory, const std::string& avsGateway) : - MessageRouter(authDelegate, attachmentManager, factory, avsGateway) { + MessageRouter( + authDelegate, + attachmentManager, + factory, + avsGateway, + avsCommon::sdkInterfaces::ENGINE_TYPE_ALEXA_VOICE_SERVICES, + SHORT_SERVER_SIDE_DISCONNECT_GRACE_PERIOD) { } /** @@ -64,11 +70,14 @@ class TestableMessageRouter : public MessageRouter { auto status = future.wait_for(millisecondsToWait); return status == std::future_status::ready; } + + /// Short amount of time to allow for an automatic reconnect before notifying of a server side disconnect. + static const std::chrono::milliseconds SHORT_SERVER_SIDE_DISCONNECT_GRACE_PERIOD; }; class MockTransportFactory : public TransportFactoryInterface { public: - MockTransportFactory(std::shared_ptr transport) : m_mockTransport{transport} { + explicit MockTransportFactory(std::shared_ptr transport) : m_mockTransport{transport} { } void setMockTransport(std::shared_ptr transport) { @@ -146,6 +155,7 @@ class MessageRouterTest : public ::testing::Test { // TestableMessageRouter m_router; }; +const std::chrono::milliseconds TestableMessageRouter::SHORT_SERVER_SIDE_DISCONNECT_GRACE_PERIOD(2000); const std::string MessageRouterTest::MESSAGE = "123456789"; const int MessageRouterTest::MESSAGE_LENGTH = 10; const std::chrono::milliseconds MessageRouterTest::SHORT_TIMEOUT_MS = std::chrono::milliseconds(1000); diff --git a/ACL/test/Transport/MockHTTP2Request.h b/ACL/test/Transport/MockHTTP2Request.h index 3ed5d70ad5..5664e1671f 100644 --- a/ACL/test/Transport/MockHTTP2Request.h +++ b/ACL/test/Transport/MockHTTP2Request.h @@ -39,7 +39,7 @@ namespace test { */ class MockHTTP2Request : public HTTP2RequestInterface { public: - MockHTTP2Request(const alexaClientSDK::avsCommon::utils::http2::HTTP2RequestConfig& config); + explicit MockHTTP2Request(const alexaClientSDK::avsCommon::utils::http2::HTTP2RequestConfig& config); MOCK_METHOD0(cancel, bool()); MOCK_CONST_METHOD0(getId, std::string()); diff --git a/ACL/test/Transport/MockMessageRouterObserver.h b/ACL/test/Transport/MockMessageRouterObserver.h index fa2b71aa61..8c0bc10529 100644 --- a/ACL/test/Transport/MockMessageRouterObserver.h +++ b/ACL/test/Transport/MockMessageRouterObserver.h @@ -31,63 +31,101 @@ namespace test { class MockMessageRouterObserver : public MessageRouterObserverInterface { public: void reset() { - notifiedOfReceive = false; - notifiedOfStatusChanged = false; + std::lock_guard lock(m_mutex); + m_notifiedOfReceive = false; + m_notifiedOfStatusChanged = false; } bool wasNotifiedOfStatusChange() { - return notifiedOfStatusChanged; + std::lock_guard lock(m_mutex); + return m_notifiedOfStatusChanged; } bool wasNotifiedOfReceive() { - return notifiedOfReceive; + std::lock_guard lock(m_mutex); + return m_notifiedOfReceive; } avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status getLatestConnectionStatus() { + std::lock_guard lock(m_mutex); return m_status; } avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::ChangedReason getLatestConnectionChangedReason() { + std::lock_guard lock(m_mutex); return m_reason; } + bool waitForStatusChange( + std::chrono::milliseconds timeout, + avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status status, + avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::ChangedReason reason) { + std::unique_lock lock(m_mutex); + auto result = m_cv.wait_for(lock, timeout, [this, status, reason]() { + return m_notifiedOfStatusChanged && m_status == status && m_reason == reason; + }); + return result; + } + std::string getLatestMessage() { + std::lock_guard lock(m_mutex); return m_message; } std::string getAttachmentContextId() { + std::lock_guard lock(m_mutex); return m_attachmentContextId; } private: - virtual void onConnectionStatusChanged( + void onConnectionStatusChanged( const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status status, const std::vector& engineConnectionStatuses) override { if (engineConnectionStatuses.empty()) { return; } - notifiedOfStatusChanged = true; + std::lock_guard lock(m_mutex); + m_notifiedOfStatusChanged = true; m_status = status; for (auto connectionStatus : engineConnectionStatuses) { if (connectionStatus.engineType == avsCommon::sdkInterfaces::ENGINE_TYPE_ALEXA_VOICE_SERVICES) { m_reason = connectionStatus.reason; } } + m_cv.notify_all(); } - virtual void receive(const std::string& contextId, const std::string& message) override { - notifiedOfReceive = true; + void receive(const std::string& contextId, const std::string& message) override { + std::lock_guard lock(m_mutex); + m_notifiedOfReceive = true; m_attachmentContextId = contextId; m_message = message; } + /// Mutex for serializing notification of status changes. + std::mutex m_mutex; + + /// Condition variable used to wait for and notify of state change notifications. + std::condition_variable m_cv; + + /// Last observed status avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status m_status; + + /// Last observerd reason avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::ChangedReason m_reason; + + /// Last observed context ID std::string m_attachmentContextId; + + /// Last observerd message std::string m_message; - bool notifiedOfStatusChanged; - bool notifiedOfReceive; + + /// Whether the observer has been notified of a stage change. + bool m_notifiedOfStatusChanged; + + /// Whether a receove was observed. + bool m_notifiedOfReceive; }; } // namespace test diff --git a/ADSL/src/DirectiveSequencer.cpp b/ADSL/src/DirectiveSequencer.cpp index af31691fe8..8a997f675f 100644 --- a/ADSL/src/DirectiveSequencer.cpp +++ b/ADSL/src/DirectiveSequencer.cpp @@ -170,7 +170,7 @@ void DirectiveSequencer::receiveDirectiveLocked(std::unique_lock& lo ACSDK_METRIC_MSG(TAG, directive, Metrics::Location::ADSL_DEQUEUE); } - bool handled = false; + bool handled; /** * Previously it was expected that all directives resulting from a Recognize event diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/Attachment/AttachmentWriter.h b/AVSCommon/AVS/include/AVSCommon/AVS/Attachment/AttachmentWriter.h index 66a92cd44c..5dcb3a46c8 100644 --- a/AVSCommon/AVS/include/AVSCommon/AVS/Attachment/AttachmentWriter.h +++ b/AVSCommon/AVS/include/AVSCommon/AVS/Attachment/AttachmentWriter.h @@ -18,6 +18,7 @@ #include #include +#include namespace alexaClientSDK { namespace avsCommon { @@ -77,6 +78,37 @@ class AttachmentWriter { virtual void close() = 0; }; +/** + * Write an @c Attachment::WriteStatus value to the given stream. + * + * @param stream The stream to write the value to. + * @param status The value to write to the stream as a string. + * @return The stream that was passed in and written to. + */ +inline std::ostream& operator<<(std::ostream& stream, const AttachmentWriter::WriteStatus& status) { + switch (status) { + case AttachmentWriter::WriteStatus::OK: + stream << "OK"; + break; + case AttachmentWriter::WriteStatus::CLOSED: + stream << "CLOSED"; + break; + case AttachmentWriter::WriteStatus::OK_BUFFER_FULL: + stream << "OK_BUFFER_FULL"; + break; + case AttachmentWriter::WriteStatus::ERROR_BYTES_LESS_THAN_WORD_SIZE: + stream << "ERROR_BYTES_LESS_THAN_WORD_SIZE"; + break; + case AttachmentWriter::WriteStatus::ERROR_INTERNAL: + stream << "ERROR_INTERNAL"; + break; + case AttachmentWriter::WriteStatus::TIMEDOUT: + stream << "TIMEDOUT"; + break; + } + return stream; +} + } // namespace attachment } // namespace avs } // namespace avsCommon diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/ComponentConfiguration.h b/AVSCommon/AVS/include/AVSCommon/AVS/ComponentConfiguration.h index c533df39f5..5b3fe4467e 100644 --- a/AVSCommon/AVS/include/AVSCommon/AVS/ComponentConfiguration.h +++ b/AVSCommon/AVS/include/AVSCommon/AVS/ComponentConfiguration.h @@ -78,7 +78,7 @@ bool operator!=(const ComponentConfiguration& lhs, const ComponentConfiguration& * @param rhs The right hand side of the == operation. * @return Whether or not this instance and @c rhs are equivalent. */ -bool operator==(const std::shared_ptr lhs, const std::shared_ptr rhs); +bool operator==(const std::shared_ptr& lhs, const std::shared_ptr& rhs); /** * Operator != for shared_ptr of @c ComponentConfiguration @@ -87,7 +87,7 @@ bool operator==(const std::shared_ptr lhs, const std::sh * @param rhs The right hand side of the != operation. * @return Whether or not this instance and @c rhs are not equivalent. */ -bool operator!=(const std::shared_ptr lhs, const std::shared_ptr rhs); +bool operator!=(const std::shared_ptr& lhs, const std::shared_ptr& rhs); } // namespace avs } // namespace avsCommon diff --git a/AVSCommon/AVS/src/AbstractAVSConnectionManager.cpp b/AVSCommon/AVS/src/AbstractAVSConnectionManager.cpp index 280a7391c5..40f3abddee 100644 --- a/AVSCommon/AVS/src/AbstractAVSConnectionManager.cpp +++ b/AVSCommon/AVS/src/AbstractAVSConnectionManager.cpp @@ -69,7 +69,6 @@ void AbstractAVSConnectionManager::addConnectionStatusObserver( // call old onConnectionStatusChanged API on AVS connection for (const auto& status : localEngineConnectionStatuses) { if (status.engineType == avsCommon::sdkInterfaces::ENGINE_TYPE_ALEXA_VOICE_SERVICES) { - ACSDK_DEBUG9(LX(__func__).d("status", status.status).d("reason", status.reason)); observer->onConnectionStatusChanged(status.status, status.reason); break; } @@ -130,7 +129,6 @@ void AbstractAVSConnectionManager::notifyObservers(bool avsConnectionStatusChang if (avsConnectionStatusChanged) { for (const auto& status : localEngineConnectionStatuses) { if (status.engineType == avsCommon::sdkInterfaces::ENGINE_TYPE_ALEXA_VOICE_SERVICES) { - ACSDK_DEBUG9(LX(__func__).d("status", status.status).d("reason", status.reason)); observer->onConnectionStatusChanged(status.status, status.reason); break; } diff --git a/AVSCommon/AVS/src/ComponentConfiguration.cpp b/AVSCommon/AVS/src/ComponentConfiguration.cpp index e1e68eb43a..d241f5f5da 100644 --- a/AVSCommon/AVS/src/ComponentConfiguration.cpp +++ b/AVSCommon/AVS/src/ComponentConfiguration.cpp @@ -86,7 +86,9 @@ bool operator!=(const ComponentConfiguration& lhs, const ComponentConfiguration& return !(lhs == rhs); } -bool operator==(const std::shared_ptr lhs, const std::shared_ptr rhs) { +bool operator==( + const std::shared_ptr& lhs, + const std::shared_ptr& rhs) { if (!lhs && !rhs) { return true; } @@ -97,7 +99,9 @@ bool operator==(const std::shared_ptr lhs, const std::sh return *lhs == *rhs; } -bool operator!=(const std::shared_ptr lhs, const std::shared_ptr rhs) { +bool operator!=( + const std::shared_ptr& lhs, + const std::shared_ptr& rhs) { return !(lhs == rhs); } diff --git a/AVSCommon/AVS/test/CapabilityAgentTest.cpp b/AVSCommon/AVS/test/CapabilityAgentTest.cpp index c84e865dca..70242ffbcb 100644 --- a/AVSCommon/AVS/test/CapabilityAgentTest.cpp +++ b/AVSCommon/AVS/test/CapabilityAgentTest.cpp @@ -244,8 +244,8 @@ class MockCapabilityAgent : public CapabilityAgent { * @return A shared pointer to an instance of the @c MockCapabilityAgent. */ static std::shared_ptr create( - const std::string nameSpace, - const std::shared_ptr m_exceptionSender); + const std::string& nameSpace, + const std::shared_ptr& m_exceptionSender); /** * MockCapabilityAgent Constructor. @@ -255,7 +255,7 @@ class MockCapabilityAgent : public CapabilityAgent { */ MockCapabilityAgent( const std::string& nameSpace, - const std::shared_ptr m_exceptionSender); + const std::shared_ptr& m_exceptionSender); ~MockCapabilityAgent() override; @@ -311,14 +311,14 @@ class MockCapabilityAgent : public CapabilityAgent { }; std::shared_ptr MockCapabilityAgent::create( - const std::string nameSpace, - const std::shared_ptr m_exceptionSender) { + const std::string& nameSpace, + const std::shared_ptr& m_exceptionSender) { return std::make_shared(nameSpace, m_exceptionSender); } MockCapabilityAgent::MockCapabilityAgent( const std::string& nameSpace, - const std::shared_ptr m_exceptionSender) : + const std::shared_ptr& m_exceptionSender) : CapabilityAgent(nameSpace, m_exceptionSender), m_functionCalled{FunctionCalled::NONE} { } diff --git a/AVSCommon/AVS/test/ExceptionEncounteredSenderTest.cpp b/AVSCommon/AVS/test/ExceptionEncounteredSenderTest.cpp index 4e75b47154..5c4f8cda3c 100644 --- a/AVSCommon/AVS/test/ExceptionEncounteredSenderTest.cpp +++ b/AVSCommon/AVS/test/ExceptionEncounteredSenderTest.cpp @@ -208,15 +208,13 @@ bool ExceptionEncounteredSenderTest::testExceptionEncounteredSucceeds( const std::string& unparsedDirective, avs::ExceptionErrorType error, const std::string& errorDescription) { - bool done = false; m_exceptionEncounteredEvent = std::make_shared(unparsedDirective, error, errorDescription); EXPECT_CALL(*m_mockMessageSender, sendMessage(_)) .WillOnce(Invoke(m_exceptionEncounteredEvent.get(), &ExceptionEncounteredEvent::verifyMessage)); m_exceptionEncounteredEvent->send(m_exceptionEncounteredSender); - done = true; - return done; + return true; } /* diff --git a/AVSCommon/CMakeLists.txt b/AVSCommon/CMakeLists.txt index 240dc34a67..e69d7bce79 100644 --- a/AVSCommon/CMakeLists.txt +++ b/AVSCommon/CMakeLists.txt @@ -52,6 +52,7 @@ add_library(AVSCommon SHARED Utils/src/HTTP2/HTTP2MimeRequestEncoder.cpp Utils/src/HTTP2/HTTP2MimeResponseDecoder.cpp Utils/src/HTTP2/HTTP2SendDataResult.cpp + Utils/src/ID3Tags/ID3v2Tags.cpp Utils/src/LibcurlUtils/CallbackData.cpp Utils/src/LibcurlUtils/CurlEasyHandleWrapper.cpp Utils/src/LibcurlUtils/CurlMultiHandleWrapper.cpp @@ -64,6 +65,7 @@ add_library(AVSCommon SHARED Utils/src/LibcurlUtils/LibcurlHTTP2ConnectionFactory.cpp Utils/src/LibcurlUtils/LibcurlHTTP2Request.cpp Utils/src/LibcurlUtils/LibcurlUtils.cpp + Utils/src/LibcurlUtils/DefaultSetCurlOptionsCallbackFactory.cpp Utils/src/Logger/ConsoleLogger.cpp Utils/src/Logger/Level.cpp Utils/src/Logger/LogEntry.cpp diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ChannelVolumeInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ChannelVolumeInterface.h index 6ca36a02bb..b6cdb972fb 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ChannelVolumeInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ChannelVolumeInterface.h @@ -71,6 +71,17 @@ class ChannelVolumeInterface { */ virtual bool setUnduckedVolume(int8_t volume) = 0; + /** + * Adjust the volume of the underlying speaker. + * If the underlying @c SpeakerInterface is ducked when this API is invoked, + * the corresponding unduckedVolume setting adjustment is reflected upon the next + * stopDucking call. + * + * @param delta the delta to change the volume by. + * @return @c true if the operation succeeded, @c false otherwise. + */ + virtual bool adjustUnduckedVolume(int8_t delta); + /** * Set the mute state of the underlying speaker. * @@ -98,6 +109,15 @@ class ChannelVolumeInterface { */ virtual Type getSpeakerType() const = 0; + /** + * Get the unique identifier of the @c ChannelVolumeInterface. + * + * @return The unique identifier. + */ + virtual std::size_t getId() const { + return std::size_t(this); + }; + /** * Destructor */ @@ -124,6 +144,28 @@ inline std::ostream& operator<<(std::ostream& stream, ChannelVolumeInterface::Ty return stream; } +inline bool ChannelVolumeInterface::adjustUnduckedVolume(int8_t delta) { + if ((delta > avsCommon::avs::speakerConstants::AVS_ADJUST_VOLUME_MAX) || + (delta < avsCommon::avs::speakerConstants::AVS_ADJUST_VOLUME_MIN)) { + return false; + } + if (0 == delta) { + /// No-op if delta is zero. + return true; + } + avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings settings; + if (!getSpeakerSettings(&settings)) { + return false; + } + int8_t volume = settings.volume + delta; + if (volume > avsCommon::avs::speakerConstants::AVS_SET_VOLUME_MAX) { + return setUnduckedVolume(avsCommon::avs::speakerConstants::AVS_SET_VOLUME_MAX); + } else if (volume < avsCommon::avs::speakerConstants::AVS_SET_VOLUME_MIN) { + return setUnduckedVolume(avsCommon::avs::speakerConstants::AVS_SET_VOLUME_MIN); + } + return setUnduckedVolume(volume); +} + } // namespace sdkInterfaces } // namespace avsCommon } // namespace alexaClientSDK diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/HTTPContentFetcherInterfaceFactoryInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/HTTPContentFetcherInterfaceFactoryInterface.h index 59b2bc4e1f..f74d8bc43b 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/HTTPContentFetcherInterfaceFactoryInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/HTTPContentFetcherInterfaceFactoryInterface.h @@ -37,6 +37,9 @@ class HTTPContentFetcherInterfaceFactoryInterface { /** * Produces an @c HTTPContentFetcherInterface object or @c nullptr on failure. + * + * @param url The url to fetch the content from. + * @return A new instance of @c HTTPContentFetcherInterface. */ virtual std::unique_ptr create(const std::string& url) = 0; }; diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/PowerResourceManagerInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/PowerResourceManagerInterface.h index 9d77a8f425..e88c902019 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/PowerResourceManagerInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/PowerResourceManagerInterface.h @@ -91,6 +91,12 @@ class PowerResourceManagerInterface { */ virtual bool isPowerResourceAcquired(const std::string& component) = 0; + /** + * Acquires the time since latest system resume. + * @return time since last system resume, if implemented by power manager, zero otherwise. + */ + virtual std::chrono::milliseconds getTimeSinceLastResumeMS(); + /** * New APIs to support refcount and acquire with timeout. * Use the below new APIs - create(), acquire(), release() and close() @@ -155,6 +161,15 @@ class PowerResourceManagerInterface { virtual bool close(const std::shared_ptr& id) = 0; }; +/** + * Provides the default @c PowerResourceManagerInterface time since last resume in MS. + * + * @return Return default value of 0 milliseconds in the form of std::chrono::milliseconds. + */ +inline std::chrono::milliseconds PowerResourceManagerInterface::getTimeSinceLastResumeMS() { + return std::chrono::milliseconds(0); +} + /** * Converts the @c PowerResourceLevel enum to a string. * diff --git a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockChannelVolumeInterface.h b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockChannelVolumeInterface.h index ae699ee6c8..ac778fbfa6 100644 --- a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockChannelVolumeInterface.h +++ b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockChannelVolumeInterface.h @@ -18,6 +18,7 @@ #include #include +#include namespace alexaClientSDK { namespace avsCommon { @@ -30,6 +31,9 @@ class MockChannelVolumeManager : public avsCommon::sdkInterfaces::ChannelVolumeI avsCommon::sdkInterfaces::ChannelVolumeInterface::Type getSpeakerType() const { return m_type; } + size_t getId() const { + return (size_t)m_speaker.get(); + } bool startDucking() { return true; } @@ -52,20 +56,25 @@ class MockChannelVolumeManager : public avsCommon::sdkInterfaces::ChannelVolumeI settings->mute = m_settings.mute; return true; } - MockChannelVolumeManager(avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type) : + MockChannelVolumeManager( + avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, + std::shared_ptr speaker) : ChannelVolumeInterface(), m_settings{avsCommon::avs::speakerConstants::AVS_SET_VOLUME_MIN, false}, - m_type{type} { + m_type{type}, + m_speaker{speaker} { } private: avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings m_settings; const avsCommon::sdkInterfaces::ChannelVolumeInterface::Type m_type; + const std::shared_ptr m_speaker; }; class MockChannelVolumeInterface : public avsCommon::sdkInterfaces::ChannelVolumeInterface { public: MOCK_CONST_METHOD0(getSpeakerType, avsCommon::sdkInterfaces::ChannelVolumeInterface::Type()); + MOCK_CONST_METHOD0(getId, size_t()); MOCK_METHOD0(startDucking, bool()); MOCK_METHOD0(stopDucking, bool()); MOCK_METHOD1(setUnduckedVolume, bool(int8_t)); @@ -74,9 +83,11 @@ class MockChannelVolumeInterface : public avsCommon::sdkInterfaces::ChannelVolum void DelegateToReal(); MockChannelVolumeInterface( avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type = - avsCommon::sdkInterfaces::ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME) : + avsCommon::sdkInterfaces::ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, + std::shared_ptr speaker = + std::make_shared>()) : ChannelVolumeInterface(), - m_manager{type} { + m_manager{type, speaker} { } private: @@ -85,6 +96,7 @@ class MockChannelVolumeInterface : public avsCommon::sdkInterfaces::ChannelVolum inline void MockChannelVolumeInterface::DelegateToReal() { ON_CALL(*this, getSpeakerType()).WillByDefault(Invoke(&m_manager, &ChannelVolumeInterface::getSpeakerType)); + ON_CALL(*this, getId()).WillByDefault(Invoke(&m_manager, &ChannelVolumeInterface::getId)); ON_CALL(*this, startDucking()).WillByDefault(Invoke(&m_manager, &ChannelVolumeInterface::startDucking)); ON_CALL(*this, stopDucking()).WillByDefault(Invoke(&m_manager, &ChannelVolumeInterface::stopDucking)); ON_CALL(*this, setUnduckedVolume(_)).WillByDefault(Invoke(&m_manager, &ChannelVolumeInterface::setUnduckedVolume)); diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Configuration/ConfigurationNode.h b/AVSCommon/Utils/include/AVSCommon/Utils/Configuration/ConfigurationNode.h index fa17a17f3b..cf57d30013 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Configuration/ConfigurationNode.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Configuration/ConfigurationNode.h @@ -135,6 +135,17 @@ class ConfigurationNode { */ bool getInt(const std::string& key, int* out = nullptr, int defaultValue = 0) const; + /** + * Get @c uint32_t value for @c key from this @c ConfigurationNode. + * + * @param key The key of the @c uint32_t value to get. + * @param[out] out Pointer to receive the returned value. + * @param defaultValue Default value to use if this @c ConfigurationNode does not have an @c uint32_t value for @c + * key. Zero if not specified. + * @return Whether this @c ConfigurationNode has an @c uint32_t value for @c key. + */ + bool getUint32(const std::string& key, uint32_t* out = nullptr, uint32_t defaultValue = 0) const; + /** * Get the @c string value for @c key from this @c ConfigurationNode. * diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/ID3Tags/ID3v2Tags.h b/AVSCommon/Utils/include/AVSCommon/Utils/ID3Tags/ID3v2Tags.h new file mode 100644 index 0000000000..799db82610 --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/ID3Tags/ID3v2Tags.h @@ -0,0 +1,47 @@ +/* + * Copyright 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_UTILS_INCLUDE_AVSCOMMON_UTILS_ID3TAGS_ID3V2TAGS_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_ID3TAGS_ID3V2TAGS_H_ + +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace id3Tags { + +/// Identifier for a ID3v2 Tag +constexpr unsigned char ID3V2TAG_IDENTIFIER[] = {'I', 'D', '3'}; + +/// The length of a ID3v2 header +constexpr unsigned int ID3V2TAG_HEADER_SIZE = 10; + +/** + * A function that reads from a unsigned char buffer and returns the length of an ID3v2 tag. This includes checking + * if the header size is valid. + * + * @param data The pointer to the unsigned char buffer. + * @param bufferSize The length of the buffer. + * @return The size of the ID3v2 tag. Return 0 is the tag is not found in buffer. + */ +std::size_t getID3v2TagSize(const unsigned char* data, std::size_t bufferSize); + +} // namespace id3Tags +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_ID3TAGS_ID3V2TAGS_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/CurlEasyHandleWrapper.h b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/CurlEasyHandleWrapper.h index 73764aba38..df11115957 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/CurlEasyHandleWrapper.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/CurlEasyHandleWrapper.h @@ -36,6 +36,28 @@ namespace alexaClientSDK { namespace avsCommon { namespace utils { namespace libcurlUtils { +// Forward declaration +class CurlEasyHandleWrapper; + +// This abstracts the setting of curl options from the CurlEasyHandleWrapper +class CurlEasyHandleWrapperOptionsSettingAdapter { +public: + CurlEasyHandleWrapperOptionsSettingAdapter(CurlEasyHandleWrapper* wrapper) : m_easyHandleWrapper(wrapper) { + } + + /** + * Helper function for calling curl_easy_setopt and checking the result. + * + * @param option The option parameter to pass through to curl_easy_setopt. + * @param param The param option to pass through to curl_easy_setopt. + * @return @c true of the operation was successful. + */ + template + bool setopt(CURLoption option, ParamType param); + +private: + CurlEasyHandleWrapper* m_easyHandleWrapper; +}; /** * Class to allocate and configure a curl easy handle @@ -213,16 +235,6 @@ class CurlEasyHandleWrapper { */ bool setReadCallback(CurlCallback callback, void* userData); - /** - * Helper function for calling curl_easy_setopt and checking the result. - * - * @param option The option parameter to pass through to curl_easy_setopt. - * @param param The param option to pass through to curl_easy_setopt. - * @return @c true of the operation was successful. - */ - template - bool setopt(CURLoption option, ParamType param); - /** * URL encode a string. * @@ -263,7 +275,19 @@ class CurlEasyHandleWrapper { */ CURLcode pause(int mask); + CurlEasyHandleWrapperOptionsSettingAdapter& curlOptionsSetter(); + private: + /** + * Helper function for calling curl_easy_setopt and checking the result. + * + * @param option The option parameter to pass through to curl_easy_setopt. + * @param param The param option to pass through to curl_easy_setopt. + * @return @c true of the operation was successful. + */ + template + bool setopt(CURLoption option, ParamType param); + /** * Frees and sets the following attributes to NULL: *
    @@ -324,6 +348,10 @@ class CurlEasyHandleWrapper { /// If no id is provided by the user, we will generate it from this counter. static std::atomic m_idGenerator; + + CurlEasyHandleWrapperOptionsSettingAdapter m_curlOptionsSettingAdapter; + + friend class CurlEasyHandleWrapperOptionsSettingAdapter; }; template @@ -341,6 +369,11 @@ bool CurlEasyHandleWrapper::setopt(CURLoption option, ParamType value) { return true; } +template +bool CurlEasyHandleWrapperOptionsSettingAdapter::setopt(CURLoption option, ParamType value) { + return m_easyHandleWrapper->setopt(option, std::forward(value)); +} + } // namespace libcurlUtils } // namespace utils } // namespace avsCommon diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/DefaultSetCurlOptionsCallbackFactory.h b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/DefaultSetCurlOptionsCallbackFactory.h new file mode 100644 index 0000000000..bac4108946 --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/DefaultSetCurlOptionsCallbackFactory.h @@ -0,0 +1,50 @@ +/* + * Copyright 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_UTILS_INCLUDE_AVSCOMMON_UTILS_LIBCURLUTILS_DEFAULTSETCURLOPTIONSCALLBACKFACTORY_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_LIBCURLUTILS_DEFAULTSETCURLOPTIONSCALLBACKFACTORY_H_ + +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace libcurlUtils { + +/** + * A default implementation of the @c LibcurlSetCurlOptionsCallbackFactoryInterface. + */ +class DefaultSetCurlOptionsCallbackFactory : public LibcurlSetCurlOptionsCallbackFactoryInterface { +public: + /** + * Create a new instance of @c DefaultSetCurlOptionsCallbackFactory. + * + * @return A new instance of @c LibcurlSetCurlOptionsCallbackFactoryInterface. + */ + static std::shared_ptr + createSetCurlOptionsCallbackFactoryInterface(); + + /** + * Constructor. + */ + DefaultSetCurlOptionsCallbackFactory() = default; +}; + +} // namespace libcurlUtils +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_LIBCURLUTILS_DEFAULTSETCURLOPTIONSCALLBACKFACTORY_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/HTTPContentFetcherFactory.h b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/HTTPContentFetcherFactory.h index 93e5a0f487..54b97a4acd 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/HTTPContentFetcherFactory.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/HTTPContentFetcherFactory.h @@ -21,6 +21,7 @@ #include #include +#include namespace alexaClientSDK { namespace avsCommon { @@ -35,22 +36,31 @@ class HTTPContentFetcherFactory : public avsCommon::sdkInterfaces::HTTPContentFe /** * Factory for creating instances of avsCommon::sdkInterfaces::HTTPContentFetcherInterfaceFactoryInterface * + * @param setCurlOptionsCallbackFactory The @c LibcurlSetCurlOptionsCallbackFactoryInterface to set user defined + * curl options. * @return A new instance of avsCommon::sdkInterfaces::HTTPContentFetcherInterfaceFactoryInterface. */ static std::shared_ptr - createHTTPContentFetcherInterfaceFactoryInterface(); + createHTTPContentFetcherInterfaceFactoryInterface( + const std::shared_ptr& setCurlOptionsCallbackFactory = nullptr); /** * Constructor. * - * @deprecated + * @param setCurlOptionsCallbackFactory The optional @c LibcurlSetCurlOptionsCallbackFactoryInterface to set user + * defined curl options */ - HTTPContentFetcherFactory() = default; + HTTPContentFetcherFactory( + const std::shared_ptr& setCurlOptionsCallbackFactory = nullptr); /// @name HTTPContentFetcherInterfaceFactoryInterface methods /// @{ std::unique_ptr create(const std::string& url) override; /// @} + +private: + /// The optional @c LibcurlSetCurlOptionsCallbackFactoryInterface to set user defined curl options. + std::shared_ptr m_setCurlOptionsCallbackFactory; }; } // namespace libcurlUtils diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibCurlHttpContentFetcher.h b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibCurlHttpContentFetcher.h index 994de9a186..908cbd6e2e 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibCurlHttpContentFetcher.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibCurlHttpContentFetcher.h @@ -23,6 +23,7 @@ #include #include +#include namespace alexaClientSDK { namespace avsCommon { @@ -35,7 +36,16 @@ namespace libcurlUtils { */ class LibCurlHttpContentFetcher : public avsCommon::sdkInterfaces::HTTPContentFetcherInterface { public: - LibCurlHttpContentFetcher(const std::string& url); + /** + * Constructor. + * + * @param url The url to fetch the content from. + * @param setCurlOptionsCallback The optional @c LibcurlSetCurlOptionsCallbackInterface allows setting user + * defined curl options. + */ + explicit LibCurlHttpContentFetcher( + const std::string& url, + const std::shared_ptr& setCurlOptionsCallback = nullptr); /// @name HTTPContentFetcherInterface methods /// @{ @@ -79,7 +89,7 @@ class LibCurlHttpContentFetcher : public avsCommon::sdkInterfaces::HTTPContentFe * @param customHeaders Custom HTTP headers to add. * @return @c curl_slist of custom headers if customHeaders are not empty, otherwise NULL. */ - curl_slist* getCustomHeaderList(std::vector customHeaders); + curl_slist* getCustomHeaderList(const std::vector& customHeaders); /// The URL to fetch from. const std::string m_url; @@ -126,7 +136,7 @@ class LibCurlHttpContentFetcher : public avsCommon::sdkInterfaces::HTTPContentFe std::thread m_thread; /// Flag to indicate that a call to @c getContent() has been made. Subsequent calls will not be accepted. - std::atomic_flag m_hasObjectBeenUsed; + std::atomic m_hasObjectBeenUsed; /// A mutex to ensure that all state transitions on the m_state variable are thead-safe. std::mutex m_stateMutex; diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2Connection.h b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2Connection.h index 60d9475dd5..570025f4b9 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2Connection.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2Connection.h @@ -24,6 +24,7 @@ #include "AVSCommon/Utils/HTTP2/HTTP2ConnectionInterface.h" #include "CurlMultiHandleWrapper.h" +#include "LibcurlSetCurlOptionsCallbackInterface.h" namespace alexaClientSDK { namespace avsCommon { @@ -37,9 +38,12 @@ class LibcurlHTTP2Connection : public avsCommon::utils::http2::HTTP2ConnectionIn /** * Create an @c LibcurlHTTP2Connection. * + * @param setCurlOptionsCallback The optional @c LibcurlSetCurlOptionsCallbackInterface to set curl + * options when a new http2 connection is being created. * @return The new @c LibcurlHTTP2Connection or nullptr if the operation fails. */ - static std::shared_ptr create(); + static std::shared_ptr create( + const std::shared_ptr& setCurlOptionsCallback = nullptr); /** * Destructor. @@ -57,9 +61,13 @@ class LibcurlHTTP2Connection : public avsCommon::utils::http2::HTTP2ConnectionIn protected: /** - * Constructor + * Constructor. + * + * @param setCurlOptionsCallback The optional @c LibcurlSetCurlOptionsCallbackInterface to set curl + * options when a new http2 connection is being created. */ - LibcurlHTTP2Connection(); + LibcurlHTTP2Connection( + const std::shared_ptr& setCurlOptionsCallback = nullptr); private: /** @@ -191,6 +199,9 @@ class LibcurlHTTP2Connection : public avsCommon::utils::http2::HTTP2ConnectionIn /// Set to true when we want to exit the network loop. bool m_isStopping; + + /// The @c LibcurlSetCurlOptionsCallbackInterface used for this connection. + std::shared_ptr m_setCurlOptionsCallback; }; } // namespace libcurlUtils diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2ConnectionFactory.h b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2ConnectionFactory.h index 8fa29ea390..faaaaa525d 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2ConnectionFactory.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2ConnectionFactory.h @@ -18,6 +18,8 @@ #include +#include "LibcurlSetCurlOptionsCallbackFactoryInterface.h" + namespace alexaClientSDK { namespace avsCommon { namespace utils { @@ -31,15 +33,32 @@ class LibcurlHTTP2ConnectionFactory : public avsCommon::utils::http2::HTTP2Conne /** * Create a new instance of @c LibcurlHTTP2ConnectionFactory. * + * @param curlOptionsCallbackFactory The optional factory for instances @c + * LibcurlSetCurlOptionsCallbackFactoryInterface. * @return A new instance of @c LibcurlHTTP2ConnectionFactory. */ static std::shared_ptr - createHTTP2ConnectionFactoryInterface(); + createHTTP2ConnectionFactoryInterface( + const std::shared_ptr& curlOptionsCallbackFactory = nullptr); /// @name HTTP2ConnectionFactoryInterface methods. /// @{ std::shared_ptr createHTTP2Connection() override; /// *} + + /** + * Constructor. + * + * @param curlOptionsCallbackFactory The optional factory for instances @c + * LibcurlSetCurlOptionsCallbackFactoryInterface. + */ + LibcurlHTTP2ConnectionFactory( + const std::shared_ptr& curlOptionsCallbackFactory = nullptr); + +private: + /// The optional @c LibcurlSetCurlOptionsCallbackFactoryInterface to set curl options when creating a new http2 + /// connection. + std::shared_ptr m_setCurlOptionsCallbackFactory; }; } // namespace libcurlUtils diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2Request.h b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2Request.h index 7f69061c11..0b3aaeaef5 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2Request.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2Request.h @@ -24,6 +24,7 @@ #include #include "CurlEasyHandleWrapper.h" +#include "LibcurlSetCurlOptionsCallbackInterface.h" namespace alexaClientSDK { namespace avsCommon { @@ -34,10 +35,16 @@ class LibcurlHTTP2Request : public alexaClientSDK::avsCommon::utils::http2::HTTP public: /** * Constructor. - * @param config + * + * @param config The @c HTTP2RequestConfig used for this request. + * @param setCurlOptionsCallback The @c LibcurlSetCurlOptionsCallbackInterface to set curl options when creating + * http2 conenction. * @param id Name used to identify this request. */ - LibcurlHTTP2Request(const alexaClientSDK::avsCommon::utils::http2::HTTP2RequestConfig& config, std::string id = ""); + LibcurlHTTP2Request( + const alexaClientSDK::avsCommon::utils::http2::HTTP2RequestConfig& config, + const std::shared_ptr& setCurlOptionsCallback, + std::string id = ""); /// @name HTTP2RequestInterface methods. /// @{ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlSetCurlOptionsCallbackFactoryInterface.h b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlSetCurlOptionsCallbackFactoryInterface.h new file mode 100644 index 0000000000..db9747a91f --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlSetCurlOptionsCallbackFactoryInterface.h @@ -0,0 +1,51 @@ +/* + * Copyright 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_UTILS_INCLUDE_AVSCOMMON_UTILS_LIBCURLUTILS_LIBCURLSETCURLOPTIONSCALLBACKFACTORYINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_LIBCURLUTILS_LIBCURLSETCURLOPTIONSCALLBACKFACTORYINTERFACE_H_ + +#include + +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace libcurlUtils { + +/** + * A class that creates a new instance of @c LibcurlSetCurlOptionsCallbackInterface. + */ +class LibcurlSetCurlOptionsCallbackFactoryInterface { +public: + /** + * Destructor. + */ + virtual ~LibcurlSetCurlOptionsCallbackFactoryInterface() = default; + + /** + * Create an instance of @c LibcurlSetCurlOptionsCallbackInterface. + * + * @return An instance of @c LibcurlSetCurlOptionsCallbackInterface. + */ + virtual std::shared_ptr createSetCurlOptionsCallback() = 0; +}; + +} // namespace libcurlUtils +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_LIBCURLUTILS_LIBCURLSETCURLOPTIONSCALLBACKFACTORYINTERFACE_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlSetCurlOptionsCallbackInterface.h b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlSetCurlOptionsCallbackInterface.h new file mode 100644 index 0000000000..1ea5a0ed5e --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlSetCurlOptionsCallbackInterface.h @@ -0,0 +1,53 @@ +/* + * Copyright 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_UTILS_INCLUDE_AVSCOMMON_UTILS_LIBCURLUTILS_LIBCURLSETCURLOPTIONSCALLBACKINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_LIBCURLUTILS_LIBCURLSETCURLOPTIONSCALLBACKINTERFACE_H_ + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace libcurlUtils { +class CurlEasyHandleWrapperOptionsSettingAdapter; + +/** + * This class allows to pass a user defined callback function for setting curl options when a new connection is created. + * The callback will be implemented in the applciation layer. + * + * + * @see https://curl.haxx.se/libcurl/c/curl_easy_setopt.html + */ +class LibcurlSetCurlOptionsCallbackInterface { +public: + /** + * Destructor. + */ + virtual ~LibcurlSetCurlOptionsCallbackInterface() = default; + + /** + * + * @param CurlEasyHandleWrapperOptionsSettingAdapter that allows setting curl options on a curl handle. + * + * @return true if the callback processing is successful, else false. + */ + virtual bool processCallback(CurlEasyHandleWrapperOptionsSettingAdapter& optionsSetter) = 0; +}; + +} // namespace libcurlUtils +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_LIBCURLUTILS_LIBCURLSETCURLOPTIONSCALLBACKINTERFACE_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/ErrorTypes.h b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/ErrorTypes.h index 2607113bf6..0878ed33ec 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/ErrorTypes.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/ErrorTypes.h @@ -76,8 +76,9 @@ enum class ErrorType { /// Invalid command MEDIA_ERROR_INVALID_COMMAND, /// Playlist error occurred - MEDIA_ERROR_PLAYLIST_ERROR - + MEDIA_ERROR_PLAYLIST_ERROR, + /// Decryption Flow failure + MEDIA_ERROR_DECRYPTION_FLOW }; /** @@ -140,6 +141,8 @@ inline std::string errorTypeToString(ErrorType errorType) { return "MEDIA_ERROR_INVALID_COMMAND"; case ErrorType::MEDIA_ERROR_PLAYLIST_ERROR: return "MEDIA_ERROR_PLAYLIST_ERROR"; + case ErrorType::MEDIA_ERROR_DECRYPTION_FLOW: + return "MEDIA_ERROR_DECRYPTION_FLOW"; } return "unknown ErrorType"; } diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PlaybackContext.h b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PlaybackContext.h index c20f2cd047..310e9dc895 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PlaybackContext.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PlaybackContext.h @@ -73,9 +73,9 @@ bool validateIfNotMalicious(const std::string& header); * Validate the headers. Delete the invalid entries * * @param[out] playbackContext to be validated and the invalid entries will be deleted. - * @return @c true if validation had no errrors, else @c false. + * @return if valid and not malicious. */ -bool validatePlaybackContextHeaders(PlaybackContext* playbackContext); +std::pair validatePlaybackContextHeaders(PlaybackContext* playbackContext); } // namespace mediaPlayer } // namespace utils diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/PlatformDefinitions.h b/AVSCommon/Utils/include/AVSCommon/Utils/PlatformDefinitions.h index c4cec99561..1a35e30792 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/PlatformDefinitions.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/PlatformDefinitions.h @@ -36,4 +36,8 @@ typedef SSIZE_T ssize_t; #define avscommon_EXPORT #endif +#if defined(_MSC_VER) +#define ACSDK_USE_RTTI ON +#endif + #endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_PLATFORMDEFINITIONS_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/SDKVersion.h b/AVSCommon/Utils/include/AVSCommon/Utils/SDKVersion.h index d1e511b79f..d909264107 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/SDKVersion.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/SDKVersion.h @@ -29,7 +29,7 @@ namespace utils { namespace sdkVersion { inline static std::string getCurrentVersion() { - return "1.21.0"; + return "1.22.0"; } inline static int getMajorVersion() { @@ -37,7 +37,7 @@ inline static int getMajorVersion() { } inline static int getMinorVersion() { - return 21; + return 22; } inline static int getPatchVersion() { diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/SystemClockMonitor.h b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/SystemClockMonitor.h index 317116b9fd..53ec8c62e0 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/SystemClockMonitor.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/SystemClockMonitor.h @@ -31,6 +31,12 @@ namespace timing { */ class SystemClockMonitor { public: + /** + * Factory method that creates a new @c SystemClockMonitor. + * @return A shared_ptr to a @c SystemClockMonitor. + */ + static std::shared_ptr createSystemClockMonitor(); + /** * Should be called when the device clock has synchronized (ex. ntp time sync) */ diff --git a/AVSCommon/Utils/src/Configuration/ConfigurationNode.cpp b/AVSCommon/Utils/src/Configuration/ConfigurationNode.cpp index d24635362a..59838ecd6c 100644 --- a/AVSCommon/Utils/src/Configuration/ConfigurationNode.cpp +++ b/AVSCommon/Utils/src/Configuration/ConfigurationNode.cpp @@ -151,6 +151,10 @@ bool ConfigurationNode::getInt(const std::string& key, int* out, int defaultValu return getValue(key, out, defaultValue, &Value::IsInt, &Value::GetInt); } +bool ConfigurationNode::getUint32(const std::string& key, uint32_t* out, uint32_t defaultValue) const { + return getValue(key, out, defaultValue, &Value::IsUint, &Value::GetUint); +} + bool ConfigurationNode::getString(const std::string& key, std::string* out, std::string defaultValue) const { const char* temp; auto result = getString(key, &temp, defaultValue.c_str()); diff --git a/AVSCommon/Utils/src/ID3Tags/ID3v2Tags.cpp b/AVSCommon/Utils/src/ID3Tags/ID3v2Tags.cpp new file mode 100644 index 0000000000..46ee629fac --- /dev/null +++ b/AVSCommon/Utils/src/ID3Tags/ID3v2Tags.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 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 + +#include "AVSCommon/Utils/ID3Tags/ID3v2Tags.h" +#include "AVSCommon/Utils/Logger/Logger.h" + +/// String to identify log entries originating from this file. +static const std::string TAG("ID3v2Tags"); + +/** + * 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) + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace id3Tags { + +/// The offset for ID3V2 Major version. +static constexpr unsigned int ID3V2TAG_VERSION_MAJOR_OFFSET = 3; + +/// The offset for ID3V2 size. +static constexpr unsigned int ID3V2TAG_SIZE_OFFSET = 6; + +std::size_t getID3v2TagSize(const unsigned char* data, std::size_t bufferSize) { + // Major version supported are 3 and 4 + const std::unordered_set supportedMajorVersion{3, 4}; + + if (!data) { + ACSDK_ERROR(LX("getID3v2TagSizeFailed").m("nullData")); + return 0; + } + + if (bufferSize < ID3V2TAG_HEADER_SIZE) { + return 0; + } + if (std::memcmp(data, ID3V2TAG_IDENTIFIER, sizeof(ID3V2TAG_IDENTIFIER)) == 0) { + // check if major version is supported + if (!supportedMajorVersion.count(data[ID3V2TAG_VERSION_MAJOR_OFFSET])) { + ACSDK_DEBUG9(LX(__func__).d("versionNotSupported", static_cast(data[ID3V2TAG_VERSION_MAJOR_OFFSET]))); + return 0; + } + + std::size_t id3v2_size = 0; + for (unsigned int i = ID3V2TAG_SIZE_OFFSET; i < ID3V2TAG_HEADER_SIZE; ++i) { + auto value = data[i]; + // most significant bit must be zero + if ((value & 0x80) != 0) { + ACSDK_DEBUG9(LX(__func__).d("mostSignificantBitNotZero", static_cast(value)).d("offset", i)); + return 0; + } + + // The ID3 tag size are stored as a 28 bits integer in 4 bytes, with the most significant bit of + // each byte ignored. + id3v2_size <<= 7; + id3v2_size += (value & 0x7f); + } + + if (id3v2_size == 0) { + ACSDK_DEBUG9(LX(__func__).m("id3v2SizeIsZero")); + return 0; + } + + // Add ID3 header size + id3v2_size += ID3V2TAG_HEADER_SIZE; + ACSDK_DEBUG9(LX("ID3v2 tag detected").d("tagSize", id3v2_size)); + return id3v2_size; + } + return 0; +} + +} // namespace id3Tags +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/src/LibcurlUtils/CurlEasyHandleWrapper.cpp b/AVSCommon/Utils/src/LibcurlUtils/CurlEasyHandleWrapper.cpp index 037bf81454..46cc6d29dc 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/CurlEasyHandleWrapper.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/CurlEasyHandleWrapper.cpp @@ -128,7 +128,8 @@ CurlEasyHandleWrapper::CurlEasyHandleWrapper(std::string id) : m_requestHeaders{nullptr}, m_postHeaders{nullptr}, m_post{nullptr}, - m_lastPost{nullptr} { + m_lastPost{nullptr}, + m_curlOptionsSettingAdapter{this} { if (m_handle == nullptr) { ACSDK_ERROR(LX("CurlEasyHandleWrapperFailed").d("reason", "curl_easy_init failed")); } else { @@ -394,6 +395,10 @@ CURLcode CurlEasyHandleWrapper::pause(int mask) { return CURLcode::CURLE_FAILED_INIT; } +CurlEasyHandleWrapperOptionsSettingAdapter& CurlEasyHandleWrapper::curlOptionsSetter() { + return m_curlOptionsSettingAdapter; +} + #ifdef ACSDK_EMIT_CURL_LOGS void CurlEasyHandleWrapper::initStreamLog() { std::string streamLogPrefix; diff --git a/AVSCommon/Utils/src/LibcurlUtils/DefaultSetCurlOptionsCallbackFactory.cpp b/AVSCommon/Utils/src/LibcurlUtils/DefaultSetCurlOptionsCallbackFactory.cpp new file mode 100644 index 0000000000..92602e584d --- /dev/null +++ b/AVSCommon/Utils/src/LibcurlUtils/DefaultSetCurlOptionsCallbackFactory.cpp @@ -0,0 +1,31 @@ +/* + * Copyright 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/Utils/LibcurlUtils/DefaultSetCurlOptionsCallbackFactory.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace libcurlUtils { + +std::shared_ptr DefaultSetCurlOptionsCallbackFactory:: + createSetCurlOptionsCallbackFactoryInterface() { + return nullptr; +} + +} // namespace libcurlUtils +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/src/LibcurlUtils/HTTPContentFetcherFactory.cpp b/AVSCommon/Utils/src/LibcurlUtils/HTTPContentFetcherFactory.cpp index 6cd909c27b..73cf0c4899 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/HTTPContentFetcherFactory.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/HTTPContentFetcherFactory.cpp @@ -34,14 +34,25 @@ static const std::string TAG("HTTPContentFetcherFactory"); #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) std::shared_ptr HTTPContentFetcherFactory:: - createHTTPContentFetcherInterfaceFactoryInterface() { - return std::make_shared(); + createHTTPContentFetcherInterfaceFactoryInterface( + const std::shared_ptr& setCurlOptionsCallbackFactory) { + return std::make_shared(setCurlOptionsCallbackFactory); +} + +HTTPContentFetcherFactory::HTTPContentFetcherFactory( + const std::shared_ptr& setCurlOptionsCallbackFactory) : + m_setCurlOptionsCallbackFactory{setCurlOptionsCallbackFactory} { } std::unique_ptr HTTPContentFetcherFactory::create( const std::string& url) { + std::shared_ptr setCurlOptionsCallback; + if (m_setCurlOptionsCallbackFactory) { + setCurlOptionsCallback = m_setCurlOptionsCallbackFactory->createSetCurlOptionsCallback(); + } + ACSDK_DEBUG9(LX(__func__).sensitive("URL", url).m("Creating a new http content fetcher")); - return avsCommon::utils::memory::make_unique(url); + return avsCommon::utils::memory::make_unique(url, setCurlOptionsCallback); } } // namespace libcurlUtils diff --git a/AVSCommon/Utils/src/LibcurlUtils/HttpPut.cpp b/AVSCommon/Utils/src/LibcurlUtils/HttpPut.cpp index e30739c9b1..8690615f36 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/HttpPut.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/HttpPut.cpp @@ -79,7 +79,7 @@ HTTPResponse HttpPut::doPut(const std::string& url, const std::vector(data.length()))) { + if (!m_curl.curlOptionsSetter().setopt(CURLOPT_INFILESIZE_LARGE, static_cast(data.length()))) { ACSDK_ERROR(LX(errorEvent).d(errorReasonKey, "unableToSetDataLength")); return httpResponse; } diff --git a/AVSCommon/Utils/src/LibcurlUtils/LibCurlHttpContentFetcher.cpp b/AVSCommon/Utils/src/LibcurlUtils/LibCurlHttpContentFetcher.cpp index a5b8cfd738..12e1f76945 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/LibCurlHttpContentFetcher.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/LibCurlHttpContentFetcher.cpp @@ -70,14 +70,15 @@ size_t LibCurlHttpContentFetcher::headerCallback(char* data, size_t size, size_t std::string line(static_cast(data), size * nmemb); std::transform(line.begin(), line.end(), line.begin(), ::tolower); - if (line.find("http") == 0) { + + if (line.compare(0, 4, "http") == 0) { // To find lines like: "HTTP/1.1 200 OK" std::istringstream iss(line); std::string httpVersion; int statusCode = 0; iss >> httpVersion >> statusCode; fetcher->m_header.responseCode = intToHTTPResponseCode(statusCode); - } else if (line.find("content-type") == 0) { + } else if (line.compare(0, 12, "content-type") == 0) { // To find lines like: "Content-Type: audio/x-mpegurl; charset=utf-8" std::istringstream iss(line); std::string contentTypeBeginning; @@ -90,13 +91,13 @@ size_t LibCurlHttpContentFetcher::headerCallback(char* data, size_t size, size_t contentType.erase(separator); } fetcher->m_header.contentType = contentType; - } else if (line.find("content-length") == 0) { + } else if (line.compare(0, 14, "content-length") == 0) { // To find lines like: "Content-Length: 12345" std::istringstream iss(line); std::string contentLengthBeginning; iss >> contentLengthBeginning >> fetcher->m_header.contentLength; ACSDK_DEBUG9(LX(__func__).d("type", "content-length").d("length", fetcher->m_header.contentLength)); - } else if (line.find("content-range") == 0) { + } else if (line.compare(0, 13, "content-range") == 0) { // To find lines like: "Content-Range: bytes 1000-3979/3980" std::istringstream iss(line); std::string contentRangeBeginning; @@ -201,16 +202,21 @@ size_t LibCurlHttpContentFetcher::noopCallback(char* data, size_t size, size_t n return 0; } -LibCurlHttpContentFetcher::LibCurlHttpContentFetcher(const std::string& url) : +LibCurlHttpContentFetcher::LibCurlHttpContentFetcher( + const std::string& url, + const std::shared_ptr& setCurlOptionsCallback) : m_state{HTTPContentFetcherInterface::State::INITIALIZED}, m_url{url}, m_effectiveUrl{url}, m_currentContentReceivedLength{0}, m_totalContentReceivedLength{0}, m_done{false}, - m_isShutdown{false} { - m_hasObjectBeenUsed.clear(); + m_isShutdown{false}, + m_hasObjectBeenUsed{false} { m_headerFuture = m_headerPromise.get_future(); + if (setCurlOptionsCallback) { + setCurlOptionsCallback->processCallback(m_curlWrapper.curlOptionsSetter()); + } } HTTPContentFetcherInterface::State LibCurlHttpContentFetcher::getState() { @@ -268,9 +274,10 @@ void LibCurlHttpContentFetcher::shutdown() { std::unique_ptr LibCurlHttpContentFetcher::getContent( FetchOptions fetchOption, - std::unique_ptr attachmentWriter, + std::unique_ptr, const std::vector& customHeaders) { - if (m_hasObjectBeenUsed.test_and_set()) { + bool notRunning = false; + if (!m_hasObjectBeenUsed.compare_exchange_strong(notRunning, true)) { ACSDK_ERROR(LX("getContentFailed").d("reason", "Object has already been used")); stateTransition(State::ERROR, false); return nullptr; @@ -301,6 +308,19 @@ std::unique_ptr LibCurlHttpContentFetcher::getCon stateTransition(State::ERROR, false); return nullptr; } + + // Use HTTP1.1 if url is not https + const std::string httpsString{"https"}; + auto httpsFound = m_url.find(httpsString, 0); + if (httpsFound != 0) { + curlReturnValue = curl_easy_setopt(m_curlWrapper.getCurlHandle(), CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + if (curlReturnValue != CURLE_OK) { + ACSDK_ERROR(LX("getContentFailed").d("reason", "setHttpVersion").d("error", curlReturnValue)); + stateTransition(State::ERROR, false); + return nullptr; + } + } + if (!m_curlWrapper.setConnectionTimeout(TIMEOUT_CONNECTION)) { ACSDK_ERROR(LX("getContentFailed").d("reason", "setConnectionTimeoutFailed")); stateTransition(State::ERROR, false); @@ -354,7 +374,7 @@ std::unique_ptr LibCurlHttpContentFetcher::getCon curlMultiHandle->addHandle(m_curlWrapper.getCurlHandle()); int numTransfersLeft = 1; - HTTPResponseCode finalResponseCode = HTTPResponseCode::HTTP_RESPONSE_CODE_UNDEFINED; + HTTPResponseCode finalResponseCode; char* contentType = nullptr; while (numTransfersLeft && !m_isShutdown) { @@ -536,7 +556,7 @@ std::unique_ptr LibCurlHttpContentFetcher::getCon return nullptr; } -curl_slist* LibCurlHttpContentFetcher::getCustomHeaderList(std::vector customHeaders) { +curl_slist* LibCurlHttpContentFetcher::getCustomHeaderList(const std::vector& customHeaders) { struct curl_slist* headers = nullptr; for (const auto& header : customHeaders) { ACSDK_DEBUG9(LX("getCustomHeaderList").d("header", header.c_str())); diff --git a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Connection.cpp b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Connection.cpp index 23be1a9b7a..eb58a7f0f9 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Connection.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Connection.cpp @@ -115,7 +115,10 @@ static bool performCurlChecks() { return true; } -LibcurlHTTP2Connection::LibcurlHTTP2Connection() : m_isStopping{false} { +LibcurlHTTP2Connection::LibcurlHTTP2Connection( + const std::shared_ptr& setCurlOptionsCallback) : + m_isStopping{false}, + m_setCurlOptionsCallback{setCurlOptionsCallback} { m_networkThread = std::thread(&LibcurlHTTP2Connection::networkLoop, this); } @@ -134,11 +137,12 @@ bool LibcurlHTTP2Connection::createMultiHandle() { return true; } -std::shared_ptr LibcurlHTTP2Connection::create() { +std::shared_ptr LibcurlHTTP2Connection::create( + const std::shared_ptr& setCurlOptionsCallback) { if (!performCurlChecks()) { return nullptr; } - return std::shared_ptr(new LibcurlHTTP2Connection()); + return std::shared_ptr(new LibcurlHTTP2Connection(setCurlOptionsCallback)); } LibcurlHTTP2Connection::~LibcurlHTTP2Connection() { @@ -265,7 +269,7 @@ void LibcurlHTTP2Connection::networkLoop() { } std::shared_ptr LibcurlHTTP2Connection::createAndSendRequest(const HTTP2RequestConfig& config) { - auto req = std::make_shared(config, config.getId()); + auto req = std::make_shared(config, m_setCurlOptionsCallback, config.getId()); addStream(req); return req; } diff --git a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2ConnectionFactory.cpp b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2ConnectionFactory.cpp index 143516fda7..ca262ee74b 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2ConnectionFactory.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2ConnectionFactory.cpp @@ -34,14 +34,26 @@ static const std::string TAG("LibcurlHTTP2ConnectionFactory"); #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) std::shared_ptr LibcurlHTTP2ConnectionFactory:: - createHTTP2ConnectionFactoryInterface() { - return std::make_shared(); + createHTTP2ConnectionFactoryInterface( + const std::shared_ptr& curlSetOptionsCallbackFactory) { + return std::make_shared(curlSetOptionsCallbackFactory); +} + +LibcurlHTTP2ConnectionFactory::LibcurlHTTP2ConnectionFactory( + const std::shared_ptr& curlSetOptionsCallbackFactory) : + m_setCurlOptionsCallbackFactory{curlSetOptionsCallbackFactory} { } std::shared_ptr LibcurlHTTP2ConnectionFactory:: createHTTP2Connection() { ACSDK_DEBUG5(LX(__func__)); - auto result = LibcurlHTTP2Connection::create(); + + std::shared_ptr setCurlOptionsCallback; + if (m_setCurlOptionsCallbackFactory) { + setCurlOptionsCallback = m_setCurlOptionsCallbackFactory->createSetCurlOptionsCallback(); + } + + auto result = LibcurlHTTP2Connection::create(setCurlOptionsCallback); return result; } diff --git a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Request.cpp b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Request.cpp index b9afdd179a..d062c37b5a 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Request.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Request.cpp @@ -137,6 +137,7 @@ CURL* LibcurlHTTP2Request::getCurlHandle() { LibcurlHTTP2Request::LibcurlHTTP2Request( const alexaClientSDK::avsCommon::utils::http2::HTTP2RequestConfig& config, + const std::shared_ptr& setCurlOptionsCallback, std::string id) : m_responseCodeReported{false}, m_activityTimeout{milliseconds::zero()}, @@ -157,10 +158,10 @@ LibcurlHTTP2Request::LibcurlHTTP2Request( m_stream.setURL(config.getUrl()); m_stream.setWriteCallback(LibcurlHTTP2Request::writeCallback, this); m_stream.setHeaderCallback(LibcurlHTTP2Request::headerCallback, this); - m_stream.setopt(CURLOPT_TCP_KEEPALIVE, 1); - m_stream.setopt(CURLOPT_STREAM_WEIGHT, config.getPriority()); + m_stream.curlOptionsSetter().setopt(CURLOPT_TCP_KEEPALIVE, 1); + m_stream.curlOptionsSetter().setopt(CURLOPT_STREAM_WEIGHT, config.getPriority()); #ifdef ACSDK_ENABLE_CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE - m_stream.setopt(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE); + m_stream.curlOptionsSetter().setopt(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE); #endif if (config.getSource()) { @@ -174,14 +175,18 @@ LibcurlHTTP2Request::LibcurlHTTP2Request( m_sink = config.getSink(); } if (config.getConnectionTimeout() != std::chrono::milliseconds::zero()) { - m_stream.setopt(CURLOPT_CONNECTTIMEOUT_MS, config.getConnectionTimeout().count()); + m_stream.curlOptionsSetter().setopt(CURLOPT_CONNECTTIMEOUT_MS, config.getConnectionTimeout().count()); } if (config.getTransferTimeout() != std::chrono::milliseconds::zero()) { - m_stream.setopt(CURLOPT_TIMEOUT_MS, config.getTransferTimeout().count()); + m_stream.curlOptionsSetter().setopt(CURLOPT_TIMEOUT_MS, config.getTransferTimeout().count()); } if (config.getActivityTimeout() != std::chrono::milliseconds::zero()) { m_activityTimeout = config.getActivityTimeout(); } + + if (setCurlOptionsCallback) { + setCurlOptionsCallback->processCallback(m_stream.curlOptionsSetter()); + } }; bool LibcurlHTTP2Request::hasProgressTimedOut() const { diff --git a/AVSCommon/Utils/src/MediaPlayer/PlaybackContext.cpp b/AVSCommon/Utils/src/MediaPlayer/PlaybackContext.cpp index f72b27cb7e..4671a74e50 100644 --- a/AVSCommon/Utils/src/MediaPlayer/PlaybackContext.cpp +++ b/AVSCommon/Utils/src/MediaPlayer/PlaybackContext.cpp @@ -54,17 +54,24 @@ bool validateIfNotMalicious(const std::string& header) { * Helper function to validate the headers. * * @param[out] headerConfig to be validated and the invalid entries will be deleted. - * @return @c true if validation had no errrors, else @c false. + * @return if valid and not malicious. */ -static bool validatePlaybackContextHeadersInternal(HeaderConfig* headerConfig) { +static std::pair validatePlaybackContextHeadersInternal(HeaderConfig* headerConfig) { bool foundInvalidHeaders = false; + bool foundMaliciousHeaders = false; for (auto entry = headerConfig->begin(); entry != headerConfig->end();) { - if (((entry->first.find(ALLOWED_PREFIX) == 0 && entry->first.length() >= MIN_KEY_LENGTH && - entry->first.length() <= MAX_KEY_LENGTH && entry->second.length() <= MAX_VALUE_LENGTH) || - (entry->first.compare(AUTHORIZATION) == 0 && entry->second.length() <= MAX_VALUE_LENGTH) || - (entry->first.compare(COOKIE) == 0 && entry->second.length() <= MAX_VALUE_LENGTH)) && - (validateIfNotMalicious(entry->first) && validateIfNotMalicious(entry->second))) { - entry++; + if (!validateIfNotMalicious(entry->first) || !validateIfNotMalicious(entry->second)) { + foundMaliciousHeaders = true; + } + if ((entry->first.find(ALLOWED_PREFIX) == 0 && entry->first.length() >= MIN_KEY_LENGTH && + entry->first.length() <= MAX_KEY_LENGTH && entry->second.length() <= MAX_VALUE_LENGTH) || + (entry->first.compare(AUTHORIZATION) == 0 && entry->second.length() <= MAX_VALUE_LENGTH) || + (entry->first.compare(COOKIE) == 0 && entry->second.length() <= MAX_VALUE_LENGTH)) { + if (!foundMaliciousHeaders) { + entry++; + } else { + entry = headerConfig->erase(entry); + } } else { entry = headerConfig->erase(entry); foundInvalidHeaders = true; @@ -76,27 +83,35 @@ static bool validatePlaybackContextHeadersInternal(HeaderConfig* headerConfig) { .d("found invalid headers:", foundInvalidHeaders) .d("HeadersSize:", headerConfig->size())); } + + if (foundMaliciousHeaders) { + ACSDK_WARN(LX("validateHeadersInternal").d("found malicious headers:", foundMaliciousHeaders)); + } + // Erase extra headers. for (auto entry = headerConfig->begin(); headerConfig->size() > MAX_ENTRIES_PER_CONFIG;) { entry = headerConfig->erase(entry); foundInvalidHeaders = true; } - return !foundInvalidHeaders; + return std::make_pair(!foundInvalidHeaders, !foundMaliciousHeaders); } -bool validatePlaybackContextHeaders(PlaybackContext* playbackContext) { +std::pair validatePlaybackContextHeaders(PlaybackContext* playbackContext) { std::vector configs = {&(playbackContext->keyConfig), &(playbackContext->manifestConfig), &(playbackContext->audioSegmentConfig), &(playbackContext->allConfig)}; - std::vector isHeaderValid(configs.size(), false); + std::vector> isHeaderValid(configs.size(), std::make_pair(false, false)); std::transform(configs.begin(), configs.end(), isHeaderValid.begin(), [](HeaderConfig* config) { return validatePlaybackContextHeadersInternal(config); }); - - return std::all_of(isHeaderValid.begin(), isHeaderValid.end(), [](bool isValid) { return isValid; }); + return std::make_pair( + std::all_of( + isHeaderValid.begin(), isHeaderValid.end(), [](std::pair isValid) { return isValid.first; }), + std::all_of( + isHeaderValid.begin(), isHeaderValid.end(), [](std::pair isValid) { return isValid.second; })); } } // namespace mediaPlayer diff --git a/AVSCommon/Utils/src/Metrics/MetricEventBuilder.cpp b/AVSCommon/Utils/src/Metrics/MetricEventBuilder.cpp index cbff4f6718..1b49ebf7f1 100644 --- a/AVSCommon/Utils/src/Metrics/MetricEventBuilder.cpp +++ b/AVSCommon/Utils/src/Metrics/MetricEventBuilder.cpp @@ -97,10 +97,7 @@ std::shared_ptr MetricEventBuilder::build() { } MetricEventBuilder& MetricEventBuilder::removeDataPoint(const std::string& key) { - if (m_dataPoints.find(key) != m_dataPoints.end()) { - m_dataPoints.erase(key); - } - + m_dataPoints.erase(key); return *this; } diff --git a/AVSCommon/Utils/src/Power/AggregatedPowerResourceManager.cpp b/AVSCommon/Utils/src/Power/AggregatedPowerResourceManager.cpp index b61f0b9c97..eb174a1993 100644 --- a/AVSCommon/Utils/src/Power/AggregatedPowerResourceManager.cpp +++ b/AVSCommon/Utils/src/Power/AggregatedPowerResourceManager.cpp @@ -159,8 +159,7 @@ bool AggregatedPowerResourceManager::acquire( // Do not dedupe acquire calls if refcount enabled. Let the application PowerResourceManagerInterface // handle that if desired. - if (callerPowerResourceInfo.isRefCounted || - (!callerPowerResourceInfo.isRefCounted && callerPowerResourceInfo.refCount == 0)) { + if (callerPowerResourceInfo.isRefCounted || callerPowerResourceInfo.refCount == 0) { callerPowerResourceInfo.refCount++; m_appPowerResourceManager->acquire(aggregatedPowerResourceId); } diff --git a/AVSCommon/Utils/src/RequiresShutdown.cpp b/AVSCommon/Utils/src/RequiresShutdown.cpp index f0d0128e60..0394d901cd 100644 --- a/AVSCommon/Utils/src/RequiresShutdown.cpp +++ b/AVSCommon/Utils/src/RequiresShutdown.cpp @@ -105,11 +105,11 @@ ShutdownMonitor::~ShutdownMonitor() { for (auto object : m_objects) { if (!object->isShutdown()) { m_destructorLogger.logAtExit( - alexaClientSDK::avsCommon::utils::logger::Level::WARN, + alexaClientSDK::avsCommon::utils::logger::Level::CRITICAL, LX("ShutdownMonitor").d("reason", "no shutdown() call").d("name: ", object->name())); } m_destructorLogger.logAtExit( - alexaClientSDK::avsCommon::utils::logger::Level::WARN, + alexaClientSDK::avsCommon::utils::logger::Level::CRITICAL, LX("ShutdownMonitor").d("reason", "never deleted").d("name", object->name())); } } @@ -123,7 +123,7 @@ RequiresShutdown::RequiresShutdown(const std::string& name) : m_name{name}, m_is RequiresShutdown::~RequiresShutdown() { if (!m_isShutdown) { - ACSDK_ERROR(LX("~RequiresShutdownFailed").d("reason", "notShutdown").d("name", name())); + ACSDK_CRITICAL(LX("~RequiresShutdownFailed").d("reason", "notShutdown").d("name", name())); } g_shutdownMonitor.remove(this); } diff --git a/AVSCommon/Utils/src/Stream/StreamFunctions.cpp b/AVSCommon/Utils/src/Stream/StreamFunctions.cpp index 4c5adc23ad..56859b02a7 100644 --- a/AVSCommon/Utils/src/Stream/StreamFunctions.cpp +++ b/AVSCommon/Utils/src/Stream/StreamFunctions.cpp @@ -32,7 +32,7 @@ std::unique_ptr streamFromData(const unsigned char* data, size_t l */ class ResourceStream : public std::istream { public: - ResourceStream(std::unique_ptr buf) : std::istream(buf.get()), m_buf(std::move(buf)) { + explicit ResourceStream(std::unique_ptr buf) : std::istream(buf.get()), m_buf(std::move(buf)) { } private: diff --git a/AVSCommon/Utils/src/SystemClockMonitor.cpp b/AVSCommon/Utils/src/SystemClockMonitor.cpp index b326918ce0..e0c9c5bdd2 100644 --- a/AVSCommon/Utils/src/SystemClockMonitor.cpp +++ b/AVSCommon/Utils/src/SystemClockMonitor.cpp @@ -31,6 +31,10 @@ namespace avsCommon { namespace utils { namespace timing { +std::shared_ptr SystemClockMonitor::createSystemClockMonitor() { + return std::make_shared(); +} + void SystemClockMonitor::notifySystemClockSynchronized() { notifyObservers(); } diff --git a/AVSCommon/Utils/src/UUIDGeneration.cpp b/AVSCommon/Utils/src/UUIDGeneration.cpp index 8984ff1728..7a41bca6bd 100644 --- a/AVSCommon/Utils/src/UUIDGeneration.cpp +++ b/AVSCommon/Utils/src/UUIDGeneration.cpp @@ -114,7 +114,7 @@ void addSeeds(const std::vector& seeds) { * @return A hex string of length @c numDigits. */ static const std::string generateHexWithReplacement( - std::independent_bits_engine& ibe, + std::independent_bits_engine& ibe, unsigned int numDigits, uint8_t replacementBits, uint8_t numReplacementBits) { @@ -165,7 +165,7 @@ static const std::string generateHexWithReplacement( * @return A hex string of length @c numDigits. */ static const std::string generateHex( - std::independent_bits_engine& ibe, + std::independent_bits_engine& ibe, unsigned int numDigits) { return generateHexWithReplacement(ibe, numDigits, 0, 0); } @@ -191,12 +191,12 @@ static void addDefaultSeedLocked() { } const std::string generateUUID() { - static std::independent_bits_engine ibe; + static std::independent_bits_engine ibe; std::unique_lock lock(g_mutex); - static int consistentEntropyReports = 0; - static double priorEntropyResult = 0; if (g_seedNeeded) { + static int consistentEntropyReports = 0; + static double priorEntropyResult = 0; addDefaultSeedLocked(); std::seed_seq seed(seedsPool.begin(), seedsPool.end()); ibe.seed(seed); diff --git a/AVSCommon/Utils/test/AVSCommon/Utils/MediaPlayer/PlaybackContextTest.cpp b/AVSCommon/Utils/test/AVSCommon/Utils/MediaPlayer/PlaybackContextTest.cpp index 9e9c3f9040..6e925748ea 100644 --- a/AVSCommon/Utils/test/AVSCommon/Utils/MediaPlayer/PlaybackContextTest.cpp +++ b/AVSCommon/Utils/test/AVSCommon/Utils/MediaPlayer/PlaybackContextTest.cpp @@ -31,7 +31,10 @@ const static std::string COOKIE = "Cookie"; TEST(PlaybackContextTest, test_validateHeadersWithEmptyAndValidData) { // Test with empty headers PlaybackContext playbackContext; - EXPECT_TRUE(validatePlaybackContextHeaders(&playbackContext)); + auto isValid = validatePlaybackContextHeaders(&playbackContext); + + EXPECT_TRUE(isValid.first); + EXPECT_TRUE(isValid.second); EXPECT_EQ(playbackContext.keyConfig.size(), 0u); EXPECT_EQ(playbackContext.manifestConfig.size(), 0u); EXPECT_EQ(playbackContext.audioSegmentConfig.size(), 0u); @@ -43,8 +46,10 @@ TEST(PlaybackContextTest, test_validateMixValidInvalidHeaders) { { PlaybackContext playbackContext; playbackContext.keyConfig[AUTHORIZATION] = "abcd"; + auto isValid = validatePlaybackContextHeaders(&playbackContext); - EXPECT_TRUE(validatePlaybackContextHeaders(&playbackContext)); + EXPECT_TRUE(isValid.first); + EXPECT_TRUE(isValid.second); EXPECT_EQ(playbackContext.keyConfig.size(), 1u); EXPECT_EQ(playbackContext.manifestConfig.size(), 0u); EXPECT_EQ(playbackContext.audioSegmentConfig.size(), 0u); @@ -54,8 +59,10 @@ TEST(PlaybackContextTest, test_validateMixValidInvalidHeaders) { { PlaybackContext playbackContext; playbackContext.keyConfig[COOKIE] = "abcd"; + auto isValid = validatePlaybackContextHeaders(&playbackContext); - EXPECT_TRUE(validatePlaybackContextHeaders(&playbackContext)); + EXPECT_TRUE(isValid.first); + EXPECT_TRUE(isValid.second); EXPECT_EQ(playbackContext.keyConfig.size(), 1u); EXPECT_EQ(playbackContext.manifestConfig.size(), 0u); EXPECT_EQ(playbackContext.audioSegmentConfig.size(), 0u); @@ -66,8 +73,10 @@ TEST(PlaybackContextTest, test_validateMixValidInvalidHeaders) { PlaybackContext playbackContext; playbackContext.keyConfig[AUTHORIZATION] = "abcd"; playbackContext.keyConfig[COOKIE] = "abcd"; + auto isValid = validatePlaybackContextHeaders(&playbackContext); - EXPECT_TRUE(validatePlaybackContextHeaders(&playbackContext)); + EXPECT_TRUE(isValid.first); + EXPECT_TRUE(isValid.second); EXPECT_EQ(playbackContext.keyConfig.size(), 2u); EXPECT_EQ(playbackContext.manifestConfig.size(), 0u); EXPECT_EQ(playbackContext.audioSegmentConfig.size(), 0u); @@ -78,8 +87,10 @@ TEST(PlaybackContextTest, test_validateMixValidInvalidHeaders) { PlaybackContext playbackContext; playbackContext.keyConfig[AUTHORIZATION] = "abcd"; playbackContext.keyConfig["xAUTHORIZATION"] = "abcd"; + auto isValid = validatePlaybackContextHeaders(&playbackContext); - EXPECT_FALSE(validatePlaybackContextHeaders(&playbackContext)); + EXPECT_FALSE(isValid.first); + EXPECT_TRUE(isValid.second); EXPECT_EQ(playbackContext.keyConfig.size(), 1u); EXPECT_EQ(playbackContext.manifestConfig.size(), 0u); } @@ -89,8 +100,10 @@ TEST(PlaybackContextTest, test_validateMixValidInvalidHeaders) { for (int i = 0; i < 25; i++) { playbackContext.audioSegmentConfig["x-" + std::to_string(i)] = "abcd" + std::to_string(i); } + auto isValid = validatePlaybackContextHeaders(&playbackContext); - EXPECT_FALSE(validatePlaybackContextHeaders(&playbackContext)); + EXPECT_FALSE(isValid.first); + EXPECT_TRUE(isValid.second); EXPECT_EQ(playbackContext.audioSegmentConfig.size(), 20u); } } @@ -101,8 +114,10 @@ TEST(PlaybackContextTest, test_validateHeadersWithMaxValueSize) { PlaybackContext playbackContext; playbackContext.allConfig[AUTHORIZATION] = R"(abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ)"; + auto isValid = validatePlaybackContextHeaders(&playbackContext); - EXPECT_FALSE(validatePlaybackContextHeaders(&playbackContext)); + EXPECT_FALSE(isValid.first); + EXPECT_TRUE(isValid.second); EXPECT_EQ(playbackContext.allConfig.size(), 0u); } // Test with Cookie header value size more than 4096 characters and valid Authorization header size @@ -111,8 +126,10 @@ TEST(PlaybackContextTest, test_validateHeadersWithMaxValueSize) { playbackContext.allConfig[AUTHORIZATION] = "abcd"; playbackContext.allConfig[COOKIE] = R"(abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ)"; + auto isValid = validatePlaybackContextHeaders(&playbackContext); - EXPECT_FALSE(validatePlaybackContextHeaders(&playbackContext)); + EXPECT_FALSE(isValid.first); + EXPECT_TRUE(isValid.second); EXPECT_EQ(playbackContext.allConfig.size(), 1u); } } @@ -125,18 +142,25 @@ TEST(PlaybackContextTest, test_headersWithMaxKeySize) { "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde" "fghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij" "klmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"] = "trktrk"; + auto isValid = validatePlaybackContextHeaders(&playbackContext); - EXPECT_FALSE(validatePlaybackContextHeaders(&playbackContext)); + EXPECT_FALSE(isValid.first); + EXPECT_TRUE(isValid.second); EXPECT_EQ(playbackContext.keyConfig.size(), 0u); } TEST(PlaybackContextTest, test_headersWithMaliciousCharacters) { PlaybackContext playbackContext; - playbackContext.keyConfig["x-\n"] = "trktrk\n"; - playbackContext.manifestConfig["x-\r"] = "trktrk\r"; - playbackContext.audioSegmentConfig[""] = ""; + playbackContext.keyConfig["x-AUTHORIZATION"] = "trktrk\n"; + playbackContext.keyConfig["x-AUTHORIZATION\n"] = "trktrk"; + playbackContext.manifestConfig["x-AUTHORIZATION\r"] = "trktrk"; + playbackContext.manifestConfig["x-AUTHORIZATION"] = "trktrk\r"; + playbackContext.audioSegmentConfig["x-AUTHORIZATION"] = ""; + + auto isValid = validatePlaybackContextHeaders(&playbackContext); - EXPECT_FALSE(validatePlaybackContextHeaders(&playbackContext)); + EXPECT_TRUE(isValid.first); + EXPECT_FALSE(isValid.second); EXPECT_EQ(playbackContext.keyConfig.size(), 0u); EXPECT_EQ(playbackContext.manifestConfig.size(), 0u); EXPECT_EQ(playbackContext.audioSegmentConfig.size(), 0u); diff --git a/AVSCommon/Utils/test/ID3v2TagsTest.cpp b/AVSCommon/Utils/test/ID3v2TagsTest.cpp new file mode 100644 index 0000000000..6ce7403b6a --- /dev/null +++ b/AVSCommon/Utils/test/ID3v2TagsTest.cpp @@ -0,0 +1,94 @@ +/* + * Copyright 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 + +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace test { + +using namespace alexaClientSDK::avsCommon::utils::id3Tags; + +TEST(ID3v2TagsTest, test_validID3TagsSuccess) { + constexpr unsigned char validID3v3Tag[] = {'I', 'D', '3', 3, 0, 0, 0, 0, 0, 1}; + auto length = getID3v2TagSize(validID3v3Tag, sizeof(validID3v3Tag)); + ASSERT_EQ(11U, length); + + constexpr unsigned char validID3v4Tag[] = {'I', 'D', '3', 4, 0, 0, 0, 0, 0, 1}; + length = getID3v2TagSize(validID3v4Tag, sizeof(validID3v4Tag)); + ASSERT_EQ(11U, length); +} + +TEST(ID3v2TagsTest, test_nullDataFailed) { + auto length = getID3v2TagSize(nullptr, 0); + ASSERT_EQ(0U, length); +} + +TEST(ID3v2TagsTest, test_validID3TagsMaxTagSizeSuccess) { + constexpr unsigned char validID3v3Tag[] = {'I', 'D', '3', 3, 0, 0, 0x7f, 0x7f, 0x7f, 0x7f}; + auto length = getID3v2TagSize(validID3v3Tag, sizeof(validID3v3Tag)); + unsigned int expectedResult = 0x0fffffff + ID3V2TAG_HEADER_SIZE; + ASSERT_EQ(expectedResult, length); +} + +TEST(ID3v2TagsTest, test_shortID3TagsFailed) { + constexpr unsigned char validID3Tag[] = {'I', 'D', '3', 4, 0, 0, 0, 0, 0}; + auto length = getID3v2TagSize(validID3Tag, sizeof(validID3Tag)); + ASSERT_EQ(0U, length); +} + +TEST(ID3v2TagsTest, test_invalidVersionID3TagsVersionFailed) { + constexpr unsigned char validID3Tag[] = {'I', 'D', '3', 5, 0, 0, 0, 0, 0, 1}; + auto length = getID3v2TagSize(validID3Tag, sizeof(validID3Tag)); + ASSERT_EQ(0U, length); +} + +TEST(ID3v2TagsTest, test_invalidID3TagsSize0Failed) { + constexpr unsigned char validID3Tag[] = {'I', 'D', '3', 4, 0, 0, 0, 0, 0, 0}; + auto length = getID3v2TagSize(validID3Tag, sizeof(validID3Tag)); + ASSERT_EQ(0U, length); +} + +TEST(ID3v2TagsTest, test_invalidID3TagsSize1Failed) { + constexpr unsigned char validID3Tag[] = {'I', 'D', '3', 4, 0, 0, 0x80, 0, 0, 1}; + auto length = getID3v2TagSize(validID3Tag, sizeof(validID3Tag)); + ASSERT_EQ(0U, length); +} + +TEST(ID3v2TagsTest, test_invalidID3TagsSize2Failed) { + constexpr unsigned char validID3Tag[] = {'I', 'D', '3', 4, 0, 0, 0, 0x80, 0, 1}; + auto length = getID3v2TagSize(validID3Tag, sizeof(validID3Tag)); + ASSERT_EQ(0U, length); +} + +TEST(ID3v2TagsTest, test_invalidID3TagsSize3Failed) { + constexpr unsigned char validID3Tag[] = {'I', 'D', '3', 4, 0, 0, 0, 0, 0x80, 1}; + auto length = getID3v2TagSize(validID3Tag, sizeof(validID3Tag)); + ASSERT_EQ(0U, length); +} + +TEST(ID3v2TagsTest, test_invalidID3TagsSize4Failed) { + constexpr unsigned char validID3Tag[] = {'I', 'D', '3', 4, 0, 0, 0, 0, 0, 0x80}; + auto length = getID3v2TagSize(validID3Tag, sizeof(validID3Tag)); + ASSERT_EQ(0U, length); +} + +} // namespace test +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h index 9b149bfd79..7b6dde9400 100644 --- a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h +++ b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h @@ -137,6 +137,7 @@ namespace defaultClient { class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerInterface { public: using DefaultClientManufactory = acsdkManufactory::Manufactory< + std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, @@ -163,6 +164,8 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr, std::shared_ptr, std::shared_ptr, + std::shared_ptr, + std::shared_ptr, acsdkManufactory::Annotated< avsCommon::sdkInterfaces::endpoints::DefaultEndpointAnnotation, avsCommon::sdkInterfaces::endpoints::EndpointBuilderInterface>, @@ -170,21 +173,23 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr, std::shared_ptr, std::shared_ptr, + std::shared_ptr, std::shared_ptr, + std::shared_ptr, std::shared_ptr, std::shared_ptr, - std::shared_ptr>; + std::shared_ptr, + std::shared_ptr, + std::shared_ptr>; /** * Creates and initializes a default AVS SDK client. To connect the client to AVS, users should make a call to * connect() after creation. * * @param manufactory @c Manufactory for creating various instances used by DefaultlClient. - * @param alertsMediaPlayer The media player to use to play alerts from. * @param bluetoothMediaPlayer The media player to play bluetooth content. * @param ringtoneMediaPlayer The media player to play Comms ringtones. * @param systemSoundMediaPlayer The media player to play system sounds. - * @param alertsSpeaker The speaker to control volume of alerts. * @param bluetoothSpeaker The speaker to control volume of bluetooth. * @param ringtoneSpeaker The speaker to control volume of Comms ringtones. * @param systemSoundSpeaker The speaker to control volume of system sounds. @@ -203,10 +208,7 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI * @param commsSpeaker The speaker to control volume of Comms calling audio. * @param sharedDataStream The stream to use which has the audio from microphone. #endif - * @param audioFactory The audioFactory is a component that provides unique audio streams. - * @param alertStorage The storage interface that will be used to store alerts. * @param notificationsStorage The storage interface that will be used to store notification indicators. - * @param deviceSettingStorage The storage interface that will be used to store device settings. * @param bluetoothStorage The storage interface that will be used to store bluetooth data. * @param alexaDialogStateObservers Observers that can be used to be notified of Alexa dialog related UX state * changes. @@ -214,26 +216,21 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI * @param isGuiSupported Whether the device supports GUI. * @param enabledConnectionRules The set of @c BluetoothDeviceConnectionRuleInterface instances used to * create the Bluetooth CA. - * @param systemTimezone Optional object used to set the system timezone. * @param firmwareVersion The firmware version to report to @c AVS or @c INVALID_FIRMWARE_VERSION. * @param sendSoftwareInfoOnConnected Whether to send SoftwareInfo upon connecting to @c AVS. * @param softwareInfoSenderObserver Object to receive notifications about sending SoftwareInfo. * @param bluetoothDeviceManager The @c BluetoothDeviceManager instance used to create the Bluetooth CA. * @param diagnostics Diagnostics interface which provides suite of APIs for diagnostic insight into SDK. * @param externalCapabilitiesBuilder Optional object used to build capabilities that are not included in the SDK. - * @param startAlertSchedulingOnInitialization Whether to start scheduling alerts after client initialization. If - * this is set to false, no alert scheduling will occur until onSystemClockSynchronized is called. * @param firstInteractionAudioProvider Optional object used in the first interaction started from * the alexa voice service * @return A @c std::unique_ptr to a DefaultClient if all went well or @c nullptr otherwise. */ static std::unique_ptr create( const std::shared_ptr& manufactory, - std::shared_ptr alertsMediaPlayer, std::shared_ptr bluetoothMediaPlayer, std::shared_ptr ringtoneMediaPlayer, std::shared_ptr systemSoundMediaPlayer, - std::shared_ptr alertsSpeaker, std::shared_ptr bluetoothSpeaker, std::shared_ptr ringtoneSpeaker, std::shared_ptr systemSoundSpeaker, @@ -254,10 +251,7 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr commsSpeaker, std::shared_ptr sharedDataStream, #endif - std::shared_ptr audioFactory, - std::shared_ptr alertStorage, std::shared_ptr notificationsStorage, - std::unique_ptr deviceSettingStorage, std::shared_ptr bluetoothStorage, std::unordered_set> alexaDialogStateObservers, @@ -267,7 +261,6 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::unordered_set> enabledConnectionRules = std::unordered_set< std::shared_ptr>(), - std::shared_ptr systemTimezone = nullptr, avsCommon::sdkInterfaces::softwareInfo::FirmwareVersion firmwareVersion = avsCommon::sdkInterfaces::softwareInfo::INVALID_FIRMWARE_VERSION, bool sendSoftwareInfoOnConnected = false, @@ -277,7 +270,6 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI nullptr, std::shared_ptr diagnostics = nullptr, const std::shared_ptr& externalCapabilitiesBuilder = nullptr, - bool startAlertSchedulingOnInitialization = true, capabilityAgents::aip::AudioProvider firstInteractionAudioProvider = capabilityAgents::aip::AudioProvider::null()); @@ -963,19 +955,14 @@ AudioInputProcessor. * Initializes the SDK and "glues" all the components together. * * @param manufactory @c Manufactory for creating various instances used by DefaultlClient. - * @param alertsMediaPlayer The media player to use to play alerts from. * @param bluetoothMediaPlayer The media player to play bluetooth content. * @param ringtoneMediaPlayer The media player to play Comms ringtones. * @param systemSoundPlayer The media player to play system sounds. - * @param alertsSpeaker The speaker to control volume of alerts. * @param bluetoothSpeaker The speaker to control bluetooth volume. * @param ringtoneSpeaker The speaker to control volume of Comms ringtones. * @param systemSoundSpeaker The speaker to control volume of system sounds. * @param additionalSpeakers A map of additional speakers to receive volume changes. - * @param audioFactory The audioFactory is a component the provides unique audio streams. - * @param alertStorage The storage interface that will be used to store alerts. * @param notificationsStorage The storage interface that will be used to store notification indicators. - * @param deviceSettingsStorage The storage interface that will be used to store device settings. * @param bluetoothStorage The storage interface that will be used to store bluetooth data. * @param alexaDialogStateObservers Observers that can be used to be notified of Alexa dialog related UX state * changes. @@ -983,27 +970,21 @@ AudioInputProcessor. * @param isGuiSupported Whether the device supports GUI. * @param enabledConnectionRules The set of @c BluetoothDeviceConnectionRuleInterface instances used to * create the Bluetooth CA. - * @param systemTimezone Optional object used to set the system timezone. * @param firmwareVersion The firmware version to report to @c AVS or @c INVALID_FIRMWARE_VERSION. * @param sendSoftwareInfoOnConnected Whether to send SoftwareInfo upon connecting to @c AVS. * @param softwareInfoSenderObserver Object to receive notifications about sending SoftwareInfo. * @param bluetoothDeviceManager The @c BluetoothDeviceManager instance used to create the Bluetooth CA. * @param diagnostics Diagnostics interface that provides suite of APIs for insights into SDK. * @param externalCapabilitiesBuilder Object used to build capabilities that are not included in the SDK. - * @param channelVolumeFactory Optional object used to build @c ChannelVolumeInterface in the SDK. - * @param startAlertSchedulingOnInitialization Whether to start scheduling alerts after client initialization. If - * this is set to false, no alert scheduling will occur until onSystemClockSynchronized is called. * @param firstInteractionAudioProvider Optional object used in the first interaction started from * the alexa voice service * @return Whether the SDK was initialized properly. */ bool initialize( const std::shared_ptr& manufactory, - std::shared_ptr alertsMediaPlayer, std::shared_ptr bluetoothMediaPlayer, std::shared_ptr ringtoneMediaPlayer, std::shared_ptr systemSoundMediaPlayer, - std::shared_ptr alertsSpeaker, std::shared_ptr bluetoothSpeaker, std::shared_ptr ringtoneSpeaker, std::shared_ptr systemSoundSpeaker, @@ -1024,10 +1005,7 @@ AudioInputProcessor. std::shared_ptr commsSpeaker, std::shared_ptr sharedDataStream, #endif - std::shared_ptr audioFactory, - std::shared_ptr alertStorage, std::shared_ptr notificationsStorage, - std::shared_ptr deviceSettingStorage, std::shared_ptr bluetoothStorage, std::unordered_set> alexaDialogStateObservers, @@ -1036,14 +1014,12 @@ AudioInputProcessor. bool isGuiSupported, std::unordered_set> enabledConnectionRules, - std::shared_ptr systemTimezone, avsCommon::sdkInterfaces::softwareInfo::FirmwareVersion firmwareVersion, bool sendSoftwareInfoOnConnected, std::shared_ptr softwareInfoSenderObserver, std::unique_ptr bluetoothDeviceManager, std::shared_ptr diagnostics, const std::shared_ptr& externalCapabilitiesBuilder, - bool startAlertSchedulingOnInitialization, capabilityAgents::aip::AudioProvider firstInteractionAudioProvider); /// The directive sequencer. @@ -1137,9 +1113,6 @@ AudioInputProcessor. /// The TemplateRuntime capability agent. std::shared_ptr m_templateRuntime; - /// The DoNotDisturb capability agent. - std::shared_ptr m_dndCapabilityAgent; - /// The Equalizer capability agent. std::shared_ptr m_equalizerCapabilityAgent; @@ -1172,9 +1145,6 @@ AudioInputProcessor. /// Module responsible for managing device settings. std::shared_ptr m_deviceSettingsManager; - /// Settings storage. This storage needs to be closed during default client destruction. - std::shared_ptr m_deviceSettingStorage; - /// DeviceInfo which reflects the device setup credentials. std::shared_ptr m_deviceInfo; diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h index f16f44788a..d450b35fc6 100644 --- a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h +++ b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h @@ -19,7 +19,9 @@ #include #include +#include #include +#include #include #include #include @@ -39,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +51,7 @@ #include #include #include +#include #include "DefaultClient/EqualizerRuntimeSetup.h" #include "DefaultClient/StubApplicationAudioPipelineFactory.h" @@ -62,6 +66,7 @@ namespace defaultClient { * non-manufactory method of initialization. */ using DefaultClientComponent = acsdkManufactory::Component< + std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, @@ -88,6 +93,8 @@ using DefaultClientComponent = acsdkManufactory::Component< std::shared_ptr, std::shared_ptr, std::shared_ptr, + std::shared_ptr, + std::shared_ptr, acsdkManufactory::Annotated< avsCommon::sdkInterfaces::endpoints::DefaultEndpointAnnotation, avsCommon::sdkInterfaces::endpoints::EndpointBuilderInterface>, @@ -95,10 +102,14 @@ using DefaultClientComponent = acsdkManufactory::Component< std::shared_ptr, std::shared_ptr, std::shared_ptr, + std::shared_ptr, std::shared_ptr, + std::shared_ptr, std::shared_ptr, std::shared_ptr, - std::shared_ptr>; + std::shared_ptr, + std::shared_ptr, + std::shared_ptr>; /** * Get the manufactory @c Component for (legacy) @c DefaultClient initialization. @@ -127,7 +138,12 @@ DefaultClientComponent getComponent( audioMediaResourceProvider, const std::shared_ptr& messageStorage, const std::shared_ptr& powerResourceManager, - const acsdkExternalMediaPlayer::ExternalMediaPlayer::AdapterCreationMap& adapterCreationMap); + const acsdkExternalMediaPlayer::ExternalMediaPlayer::AdapterCreationMap& adapterCreationMap, + const std::shared_ptr& systemTimeZone, + const std::shared_ptr& deviceSettingStorage, + bool startAlertSchedulingOnInitialization, + const std::shared_ptr& audioFactory, + const std::shared_ptr& alertStorage); } // namespace defaultClient } // namespace alexaClientSDK diff --git a/ApplicationUtilities/DefaultClient/src/CMakeLists.txt b/ApplicationUtilities/DefaultClient/src/CMakeLists.txt index 594854f3b0..e31b7880a8 100644 --- a/ApplicationUtilities/DefaultClient/src/CMakeLists.txt +++ b/ApplicationUtilities/DefaultClient/src/CMakeLists.txt @@ -5,10 +5,10 @@ add_library(DefaultClient SHARED ConnectionRetryTrigger.cpp DefaultClient.cpp DefaultClientComponent.cpp - DeviceSettingsManagerBuilder.cpp EqualizerRuntimeSetup.cpp StubApplicationAudioPipelineFactory.cpp ) + target_include_directories(DefaultClient PUBLIC "${DefaultClient_SOURCE_DIR}/include" "${DeviceSettings_SOURCE_DIR}/include" @@ -20,6 +20,7 @@ if(BLUETOOTH_BLUEZ) endif() target_link_libraries(DefaultClient + ${ACSDKDEVICESETTINGSMANAGER_LIB} ACL acsdkApplicationAudioPipelineFactoryInterfaces acsdkCore @@ -51,6 +52,7 @@ target_link_libraries(DefaultClient SystemSoundPlayer TemplateRuntime acsdkAlerts + acsdkAlertsInterfaces acsdkAudioPlayer acsdkBluetooth acsdkEqualizer diff --git a/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp b/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp index c8b3fd1030..e0e5fc2ce8 100644 --- a/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp +++ b/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp @@ -56,7 +56,6 @@ #include "DefaultClient/DefaultClient.h" #include "DefaultClient/DefaultClientComponent.h" -#include "DefaultClient/DeviceSettingsManagerBuilder.h" #include "DefaultClient/StubApplicationAudioPipelineFactory.h" #include "SDKComponent/SDKComponent.h" @@ -87,11 +86,9 @@ static const std::string VISUAL_CHANNEL_CONFIG_KEY = "visualChannels"; std::unique_ptr DefaultClient::create( const std::shared_ptr& manufactory, - std::shared_ptr alertsMediaPlayer, std::shared_ptr bluetoothMediaPlayer, std::shared_ptr ringtoneMediaPlayer, std::shared_ptr systemSoundMediaPlayer, - std::shared_ptr alertsSpeaker, std::shared_ptr bluetoothSpeaker, std::shared_ptr ringtoneSpeaker, std::shared_ptr systemSoundSpeaker, @@ -112,10 +109,7 @@ std::unique_ptr DefaultClient::create( std::shared_ptr commsSpeaker, std::shared_ptr sharedDataStream, #endif - std::shared_ptr audioFactory, - std::shared_ptr alertStorage, std::shared_ptr notificationsStorage, - std::unique_ptr deviceSettingStorage, std::shared_ptr bluetoothStorage, std::unordered_set> alexaDialogStateObservers, @@ -124,23 +118,19 @@ std::unique_ptr DefaultClient::create( bool isGuiSupported, std::unordered_set> enabledConnectionRules, - std::shared_ptr systemTimezone, avsCommon::sdkInterfaces::softwareInfo::FirmwareVersion firmwareVersion, bool sendSoftwareInfoOnConnected, std::shared_ptr softwareInfoSenderObserver, std::unique_ptr bluetoothDeviceManager, std::shared_ptr diagnostics, const std::shared_ptr& externalCapabilitiesBuilder, - bool startAlertSchedulingOnInitialization, capabilityAgents::aip::AudioProvider firstInteractionAudioProvider) { std::unique_ptr defaultClient(new DefaultClient()); if (!defaultClient->initialize( manufactory, - alertsMediaPlayer, bluetoothMediaPlayer, ringtoneMediaPlayer, systemSoundMediaPlayer, - alertsSpeaker, bluetoothSpeaker, ringtoneSpeaker, systemSoundSpeaker, @@ -159,23 +149,18 @@ std::unique_ptr DefaultClient::create( commsSpeaker, sharedDataStream, #endif - audioFactory, - alertStorage, notificationsStorage, - std::move(deviceSettingStorage), bluetoothStorage, alexaDialogStateObservers, connectionObservers, isGuiSupported, enabledConnectionRules, - systemTimezone, firmwareVersion, sendSoftwareInfoOnConnected, softwareInfoSenderObserver, std::move(bluetoothDeviceManager), diagnostics, externalCapabilitiesBuilder, - startAlertSchedulingOnInitialization, firstInteractionAudioProvider)) { return nullptr; } @@ -271,6 +256,8 @@ std::unique_ptr DefaultClient::create( /// Add pre-created speakers and media players to the stub factory. stubAudioPipelineFactory->addApplicationMediaInterfaces( acsdkNotifications::NOTIFICATIONS_MEDIA_PLAYER_NAME, notificationsMediaPlayer, notificationsSpeaker); + stubAudioPipelineFactory->addApplicationMediaInterfaces( + acsdkAlerts::ALERTS_MEDIA_PLAYER_NAME, alertsMediaPlayer, alertsSpeaker); stubAudioPipelineFactory->addApplicationMediaInterfaces( capabilityAgents::speechSynthesizer::SPEAK_MEDIA_PLAYER_NAME, speakMediaPlayer, speakSpeaker); for (const auto& adapter : adapterCreationMap) { @@ -323,7 +310,12 @@ std::unique_ptr DefaultClient::create( audioMediaPlayerFactoryAdapter, messageStorage, powerResourceManager, - adapterCreationMap); + adapterCreationMap, + systemTimezone, + std::move(deviceSettingStorage), + startAlertSchedulingOnInitialization, + audioFactory, + std::move(alertStorage)); auto manufactory = DefaultClientManufactory::create(component); auto speakerManager = manufactory->get>(); @@ -344,11 +336,9 @@ std::unique_ptr DefaultClient::create( return create( std::move(manufactory), - alertsMediaPlayer, bluetoothMediaPlayer, ringtoneMediaPlayer, systemSoundMediaPlayer, - alertsSpeaker, bluetoothSpeaker, ringtoneSpeaker, systemSoundSpeaker, @@ -367,33 +357,26 @@ std::unique_ptr DefaultClient::create( commsSpeaker, sharedDataStream, #endif - audioFactory, - alertStorage, notificationsStorage, - std::move(deviceSettingStorage), bluetoothStorage, alexaDialogStateObservers, connectionObservers, isGuiSupported, enabledConnectionRules, - systemTimezone, firmwareVersion, sendSoftwareInfoOnConnected, softwareInfoSenderObserver, std::move(bluetoothDeviceManager), diagnostics, externalCapabilitiesBuilder, - startAlertSchedulingOnInitialization, firstInteractionAudioProvider); } bool DefaultClient::initialize( const std::shared_ptr& manufactory, - std::shared_ptr alertsMediaPlayer, std::shared_ptr bluetoothMediaPlayer, std::shared_ptr ringtoneMediaPlayer, std::shared_ptr systemSoundMediaPlayer, - std::shared_ptr alertsSpeaker, std::shared_ptr bluetoothSpeaker, std::shared_ptr ringtoneSpeaker, std::shared_ptr systemSoundSpeaker, @@ -414,10 +397,7 @@ bool DefaultClient::initialize( std::shared_ptr commsSpeaker, std::shared_ptr sharedDataStream, #endif - std::shared_ptr audioFactory, - std::shared_ptr alertStorage, std::shared_ptr notificationsStorage, - std::shared_ptr deviceSettingStorage, std::shared_ptr bluetoothStorage, std::unordered_set> alexaDialogStateObservers, @@ -426,26 +406,13 @@ bool DefaultClient::initialize( bool isGuiSupported, std::unordered_set> enabledConnectionRules, - std::shared_ptr systemTimezone, avsCommon::sdkInterfaces::softwareInfo::FirmwareVersion firmwareVersion, bool sendSoftwareInfoOnConnected, std::shared_ptr softwareInfoSenderObserver, std::unique_ptr bluetoothDeviceManager, std::shared_ptr diagnostics, const std::shared_ptr& externalCapabilitiesBuilder, - bool startAlertSchedulingOnInitialization, capabilityAgents::aip::AudioProvider firstInteractionAudioProvider) { - - if (!audioFactory) { - ACSDK_ERROR(LX("initializeFailed").d("reason", "nullAudioFactory")); - return false; - } - - if (!alertsMediaPlayer) { - ACSDK_ERROR(LX("initializeFailed").d("reason", "nullAlertsMediaPlayer")); - return false; - } - if (!bluetoothMediaPlayer) { ACSDK_ERROR(LX("initializeFailed").d("reason", "nullBluetoothMediaPlayer")); return false; @@ -461,11 +428,6 @@ bool DefaultClient::initialize( return false; } - if (!deviceSettingStorage) { - ACSDK_ERROR(LX("initializeFailed").d("reason", "nullDeviceSettingStorage")); - return false; - } - // Initialize various locals from manufactory. auto metricRecorder = manufactory->get>(); if (!metricRecorder) { @@ -517,6 +479,23 @@ bool DefaultClient::initialize( ACSDK_DEBUG7(LX(__func__).m("power resource management disabled")); } + auto systemTimezone = manufactory->get>(); + if (!systemTimezone) { + ACSDK_DEBUG7(LX(__func__).m("using default null SystemTimeZone")); + } + + auto deviceSettingStorage = manufactory->get>(); + if (!deviceSettingStorage) { + ACSDK_ERROR(LX("initializeFailed").d("reason", "nullDeviceSettingStorage")); + return false; + } + + auto audioFactory = manufactory->get>(); + if (!audioFactory) { + ACSDK_ERROR(LX("initializeFailed").d("reason", "nullAudioFactory")); + return false; + } + // Initialize various members from manufactory. m_avsGatewayManager = manufactory->get>(); if (!m_avsGatewayManager) { @@ -639,12 +618,30 @@ bool DefaultClient::initialize( return false; } + m_deviceSettingsManager = manufactory->get>(); + if (!m_deviceSettingsManager) { + ACSDK_ERROR(LX("initializeFailed").d("reason", "createDeviceSettingsManagerFailed")); + return false; + } + + m_systemClockMonitor = manufactory->get>(); + if (!m_systemClockMonitor) { + ACSDK_ERROR(LX("initializeFailed").d("reason", "nullSystemClockMonitor")); + return false; + } + + m_alertsCapabilityAgent = manufactory->get>(); + if (!m_alertsCapabilityAgent) { + ACSDK_ERROR(LX("initializeFailed").d("reason", "unableToCreateAlertsCapabilityAgent")); + return false; + } + m_dialogUXStateAggregator = std::make_shared(metricRecorder); m_softwareReporterCapabilityAgent = capabilityAgents::softwareComponentReporter::SoftwareComponentReporterCapabilityAgent::create(); if (!m_softwareReporterCapabilityAgent) { - ACSDK_ERROR(LX("initializeFailed").d("reason", "nullSoftwareReporterCapabilityAgent")); + ACSDK_ERROR(LX("initializeFailed").d("reason", "nullSoftwareComponentReporterCapabilityAgent")); return false; } @@ -708,48 +705,6 @@ bool DefaultClient::initialize( return false; } - m_deviceSettingStorage = deviceSettingStorage; - if (!m_deviceSettingStorage->open()) { - ACSDK_ERROR(LX("initializeFailed").d("reason", "deviceSettingStorageOpenFailed")); - return false; - } - - /* - * Creating the DoNotDisturb Capability Agent. - * - * TODO(ACSDK-2279): Keep this here till we can inject DND setting into the DND CA. - */ - m_dndCapabilityAgent = capabilityAgents::doNotDisturb::DoNotDisturbCapabilityAgent::create( - m_exceptionSender, m_connectionManager, m_deviceSettingStorage); - - if (!m_dndCapabilityAgent) { - ACSDK_ERROR(LX("initializeFailed").d("reason", "unableToCreateDNDCapabilityAgent")); - return false; - } - - addConnectionObserver(m_dndCapabilityAgent); - - DeviceSettingsManagerBuilder settingsManagerBuilder{ - m_deviceSettingStorage, m_connectionManager, m_connectionManager, customerDataManager}; - settingsManagerBuilder.withDoNotDisturbSetting(m_dndCapabilityAgent) - .withAlarmVolumeRampSetting() - .withWakeWordConfirmationSetting() - .withSpeechConfirmationSetting() - .withTimeZoneSetting(systemTimezone) - .withNetworkInfoSetting(); - - if (localeAssetsManager->getDefaultSupportedWakeWords().empty()) { - settingsManagerBuilder.withLocaleSetting(localeAssetsManager); - } else { - settingsManagerBuilder.withLocaleAndWakeWordsSettings(localeAssetsManager); - } - - m_deviceSettingsManager = settingsManagerBuilder.build(); - if (!m_deviceSettingsManager) { - ACSDK_ERROR(LX("initializeFailed").d("reason", "createDeviceSettingsManagerFailed")); - return false; - } - /* * Creating the User Inactivity Monitor - This component is responsibly for * updating AVS of user inactivity as @@ -766,10 +721,10 @@ bool DefaultClient::initialize( systemSoundMediaPlayer, audioFactory->systemSounds()); auto wakeWordConfirmationSetting = - settingsManagerBuilder.getSetting(); + m_deviceSettingsManager->getSetting(); auto speechConfirmationSetting = - settingsManagerBuilder.getSetting(); - auto wakeWordsSetting = settingsManagerBuilder.getSetting(); + m_deviceSettingsManager->getSetting(); + auto wakeWordsSetting = m_deviceSettingsManager->getSetting(); /* * Creating the Audio Input Processor - This component is the Capability Agent @@ -855,13 +810,10 @@ bool DefaultClient::initialize( // create @c SpeakerInterfaces for each @c Type std::vector> allAvsSpeakers{systemSoundSpeaker}; - std::vector> allAlertSpeakers{alertsSpeaker}; // parse additional Speakers into the right speaker list. for (const auto& it : additionalSpeakers) { if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == it.first) { allAvsSpeakers.push_back(it.second); - } else if (ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME == it.first) { - allAlertSpeakers.push_back(it.second); } } @@ -879,7 +831,7 @@ bool DefaultClient::initialize( // create @c ChannelVolumeInterface instances for all @c SpeakerInterface instances std::vector> allAvsChannelVolumeInterfaces, - allAlertChannelVolumeInterfaces, allChannelVolumeInterfaces; + allChannelVolumeInterfaces; // create allAvsChannelVolumeInterfaces using allAvsSpeakers for (auto& it : allAvsSpeakers) { @@ -896,63 +848,13 @@ bool DefaultClient::initialize( channelVolumeFactory->createChannelVolumeInterface(ringtoneSpeaker); allAvsChannelVolumeInterfaces.push_back(ringtoneChannelVolumeInterface); - // create @c ChannelVolumeInterface for allAlertSpeakers - for (auto& it : allAlertSpeakers) { - allAlertChannelVolumeInterfaces.push_back( - channelVolumeFactory->createChannelVolumeInterface(it, ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME)); - } - allChannelVolumeInterfaces.insert( allChannelVolumeInterfaces.end(), allAvsChannelVolumeInterfaces.begin(), allAvsChannelVolumeInterfaces.end()); - allChannelVolumeInterfaces.insert( - allChannelVolumeInterfaces.end(), - allAlertChannelVolumeInterfaces.begin(), - allAlertChannelVolumeInterfaces.end()); for (const auto& channelVolumeInterface : allChannelVolumeInterfaces) { m_speakerManager->addChannelVolumeInterface(channelVolumeInterface); } - auto alertRenderer = acsdkAlerts::renderer::Renderer::create(alertsMediaPlayer, metricRecorder); - if (!alertRenderer) { - ACSDK_ERROR(LX("initializeFailed").d("reason", "unableToCreateAlarmRenderer")); - return false; - } - - /* - * Creating the Alerts Capability Agent - This component is the Capability - * Agent that implements the Alerts interface of AVS. - */ - m_alertsCapabilityAgent = acsdkAlerts::AlertsCapabilityAgent::create( - m_connectionManager, - m_connectionManager, - m_certifiedSender, - m_audioFocusManager, - m_speakerManager, - m_contextManager, - m_exceptionSender, - alertStorage, - audioFactory->alerts(), - alertRenderer, - customerDataManager, - settingsManagerBuilder.getSetting(), - m_deviceSettingsManager, - metricRecorder, - startAlertSchedulingOnInitialization); - - if (!m_alertsCapabilityAgent) { - ACSDK_ERROR(LX("initializeFailed").d("reason", "unableToCreateAlertsCapabilityAgent")); - return false; - } - - /* - * Creating the System Clock Monitor - This component notifies the time sensitive - * components when the system clock resynchronizes. - */ - m_systemClockMonitor = std::shared_ptr( - new avsCommon::utils::timing::SystemClockMonitor()); - m_systemClockMonitor->addSystemClockMonitorObserver(m_alertsCapabilityAgent); - addConnectionObserver(m_dialogUXStateAggregator); m_notificationsRenderer = @@ -1128,7 +1030,7 @@ bool DefaultClient::initialize( * Creating the TimeZone Handler - This component is responsible for handling directives related to time zones. */ auto timezoneHandler = capabilityAgents::system::TimeZoneHandler::create( - settingsManagerBuilder.getSetting(), m_exceptionSender); + m_deviceSettingsManager->getSetting(), m_exceptionSender); if (!timezoneHandler) { ACSDK_ERROR(LX("initializeFailed").d("reason", "unableToCreateTimeZoneHandler")); return false; @@ -1138,7 +1040,7 @@ bool DefaultClient::initialize( * Creating the Locale Handler - This component is responsible for handling directives related to locales. */ auto localeHandler = capabilityAgents::system::LocaleHandler::create( - m_exceptionSender, settingsManagerBuilder.getSetting()); + m_exceptionSender, m_deviceSettingsManager->getSetting()); if (!localeHandler) { ACSDK_ERROR(LX("initializeFailed").d("reason", "unableToCreateLocaleHandler")); return false; @@ -1148,7 +1050,7 @@ bool DefaultClient::initialize( * Creating the ReportState Handler - This component is responsible for the ReportState directives. */ auto reportGenerator = capabilityAgents::system::StateReportGenerator::create( - m_deviceSettingsManager, settingsManagerBuilder.getConfigurations()); + m_deviceSettingsManager, m_deviceSettingsManager->getConfigurations()); if (!reportGenerator.hasValue()) { ACSDK_ERROR(LX("initializeFailed").d("reason", "unableToCreateStateReportGenerator")); return false; @@ -1272,7 +1174,6 @@ bool DefaultClient::initialize( */ m_defaultEndpointBuilder->withCapability(m_speechSynthesizer, m_speechSynthesizer); m_defaultEndpointBuilder->withCapability(m_audioInputProcessor, m_audioInputProcessor); - m_defaultEndpointBuilder->withCapability(m_alertsCapabilityAgent, m_alertsCapabilityAgent); m_defaultEndpointBuilder->withCapability(m_apiGatewayCapabilityAgent, m_apiGatewayCapabilityAgent); #ifdef ENABLE_PCC if (m_phoneCallControllerCapabilityAgent) { @@ -1304,8 +1205,6 @@ bool DefaultClient::initialize( m_defaultEndpointBuilder->withCapability(m_equalizerCapabilityAgent, m_equalizerCapabilityAgent); } - m_defaultEndpointBuilder->withCapability(m_dndCapabilityAgent, m_dndCapabilityAgent); - // System CA is split into multiple directive handlers. m_defaultEndpointBuilder->withCapabilityConfiguration(systemCapabilityProvider); if (!m_directiveSequencer->addDirectiveHandler(std::move(localeHandler)) || @@ -1320,7 +1219,7 @@ bool DefaultClient::initialize( } if (externalCapabilitiesBuilder) { - externalCapabilitiesBuilder->withSettingsStorage(m_deviceSettingStorage); + externalCapabilitiesBuilder->withSettingsStorage(deviceSettingStorage); externalCapabilitiesBuilder->withInternetConnectionMonitor(m_internetConnectionMonitor); externalCapabilitiesBuilder->withDialogUXStateAggregator(m_dialogUXStateAggregator); if (isGuiSupported) { @@ -1904,11 +1803,6 @@ DefaultClient::~DefaultClient() { ACSDK_DEBUG5(LX("SpeechSynthesizerShutdown")); m_speechSynthesizer->shutdown(); } - if (m_alertsCapabilityAgent) { - m_systemClockMonitor->removeSystemClockMonitorObserver(m_alertsCapabilityAgent); - ACSDK_DEBUG5(LX("AlertsShutdown")); - m_alertsCapabilityAgent->shutdown(); - } if (m_softwareInfoSender) { ACSDK_DEBUG5(LX("SoftwareInfoShutdown")); m_softwareInfoSender->shutdown(); @@ -1960,11 +1854,6 @@ DefaultClient::~DefaultClient() { m_meetingClientControllerCapabilityAgent->shutdown(); } #endif - if (m_dndCapabilityAgent) { - ACSDK_DEBUG5(LX("DNDCapabilityAgentShutdown")); - removeConnectionObserver(m_dndCapabilityAgent); - m_dndCapabilityAgent->shutdown(); - } if (nullptr != m_equalizerCapabilityAgent) { for (auto& equalizer : m_equalizerRuntimeSetup->getAllEqualizers()) { m_equalizerController->unregisterEqualizer(equalizer); @@ -1976,11 +1865,6 @@ DefaultClient::~DefaultClient() { m_equalizerCapabilityAgent->shutdown(); } - if (m_deviceSettingStorage) { - ACSDK_DEBUG5(LX("CloseSettingStorage")); - m_deviceSettingStorage->close(); - } - if (m_diagnostics) { m_diagnostics->setDiagnosticDependencies(nullptr, nullptr, nullptr); diff --git a/ApplicationUtilities/DefaultClient/src/DefaultClientComponent.cpp b/ApplicationUtilities/DefaultClient/src/DefaultClientComponent.cpp index abff0b2df4..7731d8f4d3 100644 --- a/ApplicationUtilities/DefaultClient/src/DefaultClientComponent.cpp +++ b/ApplicationUtilities/DefaultClient/src/DefaultClientComponent.cpp @@ -14,18 +14,21 @@ */ #include -#include -#include -#include - +#include #include +#include +#include #include +#include #include +#include #include #include #include #include #include +#include +#include #include #include #include @@ -57,7 +60,6 @@ using namespace acsdkShutdownManagerInterfaces; using namespace avsCommon::avs::attachment; using namespace avsCommon::utils; using namespace avsCommon::utils::libcurlUtils; -using namespace avsGatewayManager; using namespace avsGatewayManager::storage; using namespace capabilityAgents::alexa; @@ -147,6 +149,28 @@ getCreateApplicationAudioPipelineFactory( }; } +/** + * Returns an std::function that finishes configuring the @c DeviceSettingStorageInterface. + * @param DeviceSettingStorageInterface The storage interface to finish configuring. + * @return A shared pointer to an @c DeviceSettingStorageInterface. + */ +static std::function()> +getCreateDeviceSettingStorageInterface(std::shared_ptr storage) { + return [storage]() -> std::shared_ptr { + if (!storage) { + ACSDK_ERROR(LX("getCreateDeviceSettingStorageInterfaceFailed").d("isStorageNull", !storage)); + return nullptr; + } + + if (!storage->open()) { + ACSDK_ERROR(LX("getCreateDeviceSettingStorageInterfaceFailed").d("reason", "failed to open")); + return nullptr; + } + + return storage; + }; +} + DefaultClientComponent getComponent( const std::shared_ptr& authDelegate, const std::shared_ptr& contextManager, @@ -169,7 +193,12 @@ DefaultClientComponent getComponent( audioMediaResourceProvider, const std::shared_ptr& messageStorage, const std::shared_ptr& powerResourceManager, - const acsdkExternalMediaPlayer::ExternalMediaPlayer::AdapterCreationMap& adapterCreationMap) { + const acsdkExternalMediaPlayer::ExternalMediaPlayer::AdapterCreationMap& adapterCreationMap, + const std::shared_ptr& systemTimeZone, + const std::shared_ptr& deviceSettingStorage, + bool startAlertSchedulingOnInitialization, + const std::shared_ptr& audioFactory, + const std::shared_ptr& alertStorage) { return ComponentAccumulator<>() /// Instead of using factory methods to instantiate these interfaces, DefaultClientComponent accepts pre-made /// implementations and adds them to the manufactory. @@ -191,9 +220,14 @@ DefaultClientComponent getComponent( .addInstance(audioMediaResourceProvider) .addInstance(messageStorage) .addInstance(powerResourceManager) + .addInstance(systemTimeZone) + .addInstance(audioFactory) + .addInstance(alertStorage) .addRetainedFactory(getCreateApplicationAudioPipelineFactory(stubAudioPipelineFactory)) + .addRetainedFactory(getCreateDeviceSettingStorageInterface(deviceSettingStorage)) /// Baseline SDK components. Applications are not expected to modify these. + .addComponent(acsdkDeviceSettingsManager::getComponent()) .addComponent(acsdkShared::getComponent()) .addRetainedFactory(afml::interruptModel::InterruptModel::createInterruptModel) .addComponent(afml::getComponent()) @@ -209,6 +243,7 @@ DefaultClientComponent getComponent( .addRetainedFactory(AttachmentManager::createInProcessAttachmentManagerInterface) .addRetainedFactory(certifiedSender::CertifiedSender::create) .addRetainedFactory(createAlexaEventProcessedNotifierInterface) + .addRetainedFactory(DefaultSetCurlOptionsCallbackFactory::createSetCurlOptionsCallbackFactoryInterface) .addRetainedFactory(HTTPContentFetcherFactory::createHTTPContentFetcherInterfaceFactoryInterface) .addRetainedFactory(getCreateMessageRouter(messageRouterFactory)) .addUniqueFactory(capabilitiesDelegate::storage::SQLiteCapabilitiesDelegateStorage:: @@ -218,7 +253,9 @@ DefaultClientComponent getComponent( .addComponent(captions::getComponent()) /// Capability Agents. Most CAs are still instantiated in DefaultClient.cpp. + .addComponent(acsdkAlerts::getComponent(startAlertSchedulingOnInitialization)) .addComponent(acsdkAudioPlayer::getBackwardsCompatibleComponent()) + .addComponent(acsdkDoNotDisturb::getComponent()) .addComponent(acsdkExternalMediaPlayer::getBackwardsCompatibleComponent(adapterCreationMap)) .addComponent(capabilityAgents::playbackController::getComponent()) .addComponent(capabilityAgents::speakerManager::getComponent()) diff --git a/ApplicationUtilities/Resources/Audio/include/Audio/AudioFactory.h b/ApplicationUtilities/Resources/Audio/include/Audio/AudioFactory.h index 254119aa67..d742717c32 100644 --- a/ApplicationUtilities/Resources/Audio/include/Audio/AudioFactory.h +++ b/ApplicationUtilities/Resources/Audio/include/Audio/AudioFactory.h @@ -29,10 +29,18 @@ namespace resources { namespace audio { /** - * A class that constructs an object to create unique streams to the audio data for the Alerts. + * A class that constructs an object to create unique streams to the audio data for alerts and other audio types. + * */ class AudioFactory : public avsCommon::sdkInterfaces::audio::AudioFactoryInterface { public: + /** + * Factory method that creates a new @c AudioFactoryInterface. + * + * @return A shared_ptr to a new @c AudioFactoryInterface. + */ + static std::shared_ptr createAudioFactoryInterface(); + std::shared_ptr alerts() const override; std::shared_ptr notifications() const override; std::shared_ptr communications() diff --git a/ApplicationUtilities/Resources/Audio/src/AudioFactory.cpp b/ApplicationUtilities/Resources/Audio/src/AudioFactory.cpp index a9994c87c1..7822ba42a3 100644 --- a/ApplicationUtilities/Resources/Audio/src/AudioFactory.cpp +++ b/ApplicationUtilities/Resources/Audio/src/AudioFactory.cpp @@ -25,6 +25,10 @@ namespace applicationUtilities { namespace resources { namespace audio { +std::shared_ptr AudioFactory::createAudioFactoryInterface() { + return std::make_shared(); +} + std::shared_ptr AudioFactory::alerts() const { return std::make_shared(); } diff --git a/BluetoothImplementations/BlueZ/src/GVariantTupleReader.cpp b/BluetoothImplementations/BlueZ/src/GVariantTupleReader.cpp index 98538ef4dc..da5a9a3b3e 100644 --- a/BluetoothImplementations/BlueZ/src/GVariantTupleReader.cpp +++ b/BluetoothImplementations/BlueZ/src/GVariantTupleReader.cpp @@ -136,8 +136,7 @@ ManagedGVariant GVariantTupleReader::getVariant(gsize index) const { ACSDK_ERROR(LX("getVariantFailed").d("reason", "index out of range")); return ManagedGVariant(); } - GVariant* value = nullptr; - value = g_variant_get_child_value(m_tuple, index); + GVariant* value = g_variant_get_child_value(m_tuple, index); return ManagedGVariant(value); } diff --git a/CHANGELOG.md b/CHANGELOG.md index d0f7e9ce75..020a057b48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ ## ChangeLog +### Version 1.22.0 - December 8 2020 +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) + ### Version 1.21.0 - October 26 2020 -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) +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) ### Version 1.20.1 - August 6 2020 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) diff --git a/CMakeLists.txt b/CMakeLists.txt index 21c6854306..dde4782046 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) # Set project information -project(AlexaClientSDK VERSION 1.21.0 LANGUAGES CXX) +project(AlexaClientSDK VERSION 1.22.0 LANGUAGES CXX) set(PROJECT_BRIEF "A cross-platform, modular SDK for interacting with the Alexa Voice Service") # This variable should be used by extension packages to include cmake files from this project. diff --git a/CapabilitiesDelegate/include/CapabilitiesDelegate/Storage/SQLiteCapabilitiesDelegateStorage.h b/CapabilitiesDelegate/include/CapabilitiesDelegate/Storage/SQLiteCapabilitiesDelegateStorage.h index 9c03bf0cf3..7be7e7728e 100644 --- a/CapabilitiesDelegate/include/CapabilitiesDelegate/Storage/SQLiteCapabilitiesDelegateStorage.h +++ b/CapabilitiesDelegate/include/CapabilitiesDelegate/Storage/SQLiteCapabilitiesDelegateStorage.h @@ -97,6 +97,20 @@ class SQLiteCapabilitiesDelegateStorage : public CapabilitiesDelegateStorageInte */ bool eraseLocked(const std::string& endpointId); + /** + * Utility method to create a endpoint config table. + * @Note: This method is not thread safe. + * + * @return True if create table is successful, else false. + */ + bool createEndpointConfigTableLocked(); + + /** + * Utility method to close the underlying database. + * @Note: This method is not thread safe. + */ + void closeLocked(); + /// Mutex to synchronize access to database. std::mutex m_mutex; diff --git a/CapabilitiesDelegate/include/CapabilitiesDelegate/Utils/DiscoveryUtils.h b/CapabilitiesDelegate/include/CapabilitiesDelegate/Utils/DiscoveryUtils.h index 33205f1bcb..86d27fecaf 100644 --- a/CapabilitiesDelegate/include/CapabilitiesDelegate/Utils/DiscoveryUtils.h +++ b/CapabilitiesDelegate/include/CapabilitiesDelegate/Utils/DiscoveryUtils.h @@ -53,6 +53,19 @@ bool validateCapabilityConfiguration(const avsCommon::avs::CapabilityConfigurati */ bool validateEndpointAttributes(const avsCommon::avs::AVSDiscoveryEndpointAttributes& endpointAttributes); +/** + * Compares given endpoint configuration JSONs and returns true if they are equal, else false. + * @Note: RapidJSON's equal to operator compares elements of arrays in order. To overcome this, we always sort + * array entries before creating JSON strings. + * + * @param firstEndpointConfigJson The first endpoint config json string. + * @param secondEndpointConfigJson The second endpoint config json string. + * @return True if the json strings are same, else false. + */ +bool compareEndpointConfigurations( + const std::string& firstEndpointConfigJson, + const std::string& secondEndpointConfigJson); + /** * Formats the given @c EndpointAttributes and @c CapabilityConfigurations into a JSON required to send in the * @c Discovery.AddOrUpdateReport event. diff --git a/CapabilitiesDelegate/src/CapabilitiesDelegate.cpp b/CapabilitiesDelegate/src/CapabilitiesDelegate.cpp index dc21d3be98..6e8e726c66 100644 --- a/CapabilitiesDelegate/src/CapabilitiesDelegate.cpp +++ b/CapabilitiesDelegate/src/CapabilitiesDelegate.cpp @@ -159,9 +159,6 @@ void CapabilitiesDelegate::clearData() { } bool CapabilitiesDelegate::init() { - const std::string errorEvent = "initFailed"; - std::string infoEvent = "CapabilitiesDelegateInit"; - if (!m_capabilitiesDelegateStorage->open()) { ACSDK_INFO(LX(__func__).m("Couldn't open database. Creating.")); if (!m_capabilitiesDelegateStorage->createDatabase()) { @@ -787,7 +784,7 @@ void CapabilitiesDelegate::filterUnchangedPendingAddOrUpdateEndpointsLocked( for (auto& endpointIdToConfigPair : addOrUpdateEndpointIdToConfigPairs) { auto storedEndpointConfigId = storedEndpointConfig->find(endpointIdToConfigPair.first); if (storedEndpointConfig->end() != storedEndpointConfigId) { - if (endpointIdToConfigPair.second == storedEndpointConfigId->second) { + if (compareEndpointConfigurations(endpointIdToConfigPair.second, storedEndpointConfigId->second)) { ACSDK_DEBUG9(LX(__func__) .d("step", "endpoint not be included in addOrUpdateReport") .sensitive("endpointId", endpointIdToConfigPair.first)); diff --git a/CapabilitiesDelegate/src/DiscoveryEventSender.cpp b/CapabilitiesDelegate/src/DiscoveryEventSender.cpp index f098ae6e81..f957b418aa 100644 --- a/CapabilitiesDelegate/src/DiscoveryEventSender.cpp +++ b/CapabilitiesDelegate/src/DiscoveryEventSender.cpp @@ -185,7 +185,7 @@ MessageRequestObserverInterface::Status DiscoveryEventSender::sendDiscoveryEvent const std::shared_ptr& messageSender, const std::string& eventString, bool waitForEventProcessed) { - ACSDK_DEBUG5(LX(__func__)); + ACSDK_DEBUG5(LX(__func__).sensitive("discoveryEvent", eventString)); std::unique_lock lock{m_mutex}; m_eventProcessedWaitEvent.reset(); m_messageRequest.reset(); diff --git a/CapabilitiesDelegate/src/Storage/SQLiteCapabilitiesDelegateStorage.cpp b/CapabilitiesDelegate/src/Storage/SQLiteCapabilitiesDelegateStorage.cpp index 918be98435..0fb734095d 100644 --- a/CapabilitiesDelegate/src/Storage/SQLiteCapabilitiesDelegateStorage.cpp +++ b/CapabilitiesDelegate/src/Storage/SQLiteCapabilitiesDelegateStorage.cpp @@ -96,9 +96,8 @@ bool SQLiteCapabilitiesDelegateStorage::createDatabase() { return false; } - if (!m_database.performQuery(CREATE_ENDPOINT_CONFIG_TABLE_SQL_STRING)) { - ACSDK_ERROR(LX("createDatabaseFailed").d("reason", "unable to create Endpoint Config table.")); - close(); + if (!createEndpointConfigTableLocked()) { + closeLocked(); return false; } @@ -107,14 +106,44 @@ bool SQLiteCapabilitiesDelegateStorage::createDatabase() { bool SQLiteCapabilitiesDelegateStorage::open() { ACSDK_DEBUG5(LX(__func__)); std::lock_guard lock{m_mutex}; - return m_database.open(); + + if (!m_database.open()) { + ACSDK_ERROR(LX("openFailed").d("reason", "failed to open database")); + return false; + } + + /// Check if the endpoint config table exists. If not, create the table. + if (!m_database.tableExists(ENDPOINT_CONFIG_TABLE_NAME)) { + ACSDK_ERROR(LX(__func__).m("Endpoint Config Table not found. Creating...")); + if (!createEndpointConfigTableLocked()) { + closeLocked(); + return false; + } + } + + return true; } void SQLiteCapabilitiesDelegateStorage::close() { ACSDK_DEBUG5(LX(__func__)); std::lock_guard lock{m_mutex}; + closeLocked(); +} + +void SQLiteCapabilitiesDelegateStorage::closeLocked() { + ACSDK_DEBUG5(LX(__func__)); m_database.close(); } +bool SQLiteCapabilitiesDelegateStorage::createEndpointConfigTableLocked() { + if (!m_database.performQuery(CREATE_ENDPOINT_CONFIG_TABLE_SQL_STRING)) { + ACSDK_ERROR(LX("openFailed").d("reason", "unable to create Endpoint Config table.")); + closeLocked(); + return false; + } + + return true; +} + bool SQLiteCapabilitiesDelegateStorage::storeLocked(const std::string& endpointId, const std::string& endpointConfig) { ACSDK_DEBUG5(LX(__func__)); const std::string sqlString = "REPLACE INTO " + ENDPOINT_CONFIG_TABLE_NAME + " (" + diff --git a/CapabilitiesDelegate/src/Utils/DiscoveryUtils.cpp b/CapabilitiesDelegate/src/Utils/DiscoveryUtils.cpp index f19eaad096..62c7dcdb3b 100644 --- a/CapabilitiesDelegate/src/Utils/DiscoveryUtils.cpp +++ b/CapabilitiesDelegate/src/Utils/DiscoveryUtils.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -142,6 +143,38 @@ static std::string getScopeJson(const std::string& authToken) { return scopeGenerator.toString(); } +/** + * Adds the given string array as a sorted members array with the given key to the @c JsonGenerator. + * + * @param generator The @c JsonGenerator used to generate the JSON. + * @param key The key to use for the members array entry. + * @param memberArray The string array where each entry represents a member. + */ +static void addSortedMembersArray( + JsonGenerator& generator, + const std::string& key, + const std::vector& memberArray) { + auto copy = memberArray; + std::sort(copy.begin(), copy.end()); + generator.addMembersArray(key, copy); +} + +/** + * Adds the given string array as a sorted string array with the given key to the @c JsonGenerator. + * + * @param generator The @c JsonGenerator used to generate the JSON. + * @param key The key to use for the string array entry. + * @param stringArray The string array to be added. + */ +static void addSortedStringArray( + JsonGenerator& generator, + const std::string& key, + const std::vector& stringArray) { + auto copy = stringArray; + std::sort(copy.begin(), copy.end()); + generator.addStringArray(key, copy); +} + /** * Formats the given @c CapabilityConfiguration into a JSON required to send in @c Discovery.AddOrUpdateReport event. * Note: The caller should validate the CapabilityConfiguration before calling this method. @@ -166,7 +199,7 @@ static std::string getCapabilityConfigJson(const CapabilityConfiguration& capabi generator2.addMember(CAPABILITY_INTERFACE_PROPERTIES_NAME_KEY, supported); supportedJson.push_back(generator2.toString()); } - generator.addMembersArray(CAPABILITY_INTERFACE_PROPERTIES_SUPPORTED_KEY, supportedJson); + addSortedMembersArray(generator, CAPABILITY_INTERFACE_PROPERTIES_SUPPORTED_KEY, supportedJson); generator.addMember( CAPABILITY_INTERFACE_PROPERTIES_PROACTIVELY_REPORTED_KEY, capabilityConfig.properties.value().isProactivelyReported); @@ -244,6 +277,22 @@ bool validateEndpointAttributes(const AVSDiscoveryEndpointAttributes& endpointAt return true; } +bool compareEndpointConfigurations(const std::string& firstEndpointJson, const std::string& secondEndpointJson) { + rapidjson::Document firstEndpointDocument; + if (!jsonUtils::parseJSON(firstEndpointJson, &firstEndpointDocument)) { + ACSDK_ERROR(LX("compareEndpointConfigurationsFailed").d("reason", "invalid first endpoint json")); + return false; + } + + rapidjson::Document secondEndpointDocument; + if (!jsonUtils::parseJSON(secondEndpointJson, &secondEndpointDocument)) { + ACSDK_ERROR(LX("compareEndpointConfigurationsFailed").d("reason", "invalid second endpoint json")); + return false; + } + + return (firstEndpointDocument == secondEndpointDocument); +} + std::string getEndpointConfigJson( const AVSDiscoveryEndpointAttributes& endpointAttributes, const std::vector& capabilities) { @@ -253,8 +302,7 @@ std::string getEndpointConfigJson( generator.addMember(FRIENDLY_NAME_KEY, endpointAttributes.friendlyName); generator.addMember(DESCRIPTION_KEY, endpointAttributes.description); generator.addMember(MANUFACTURER_NAME_KEY, endpointAttributes.manufacturerName); - - generator.addStringArray(DISPLAY_CATEGORIES_KEY, endpointAttributes.displayCategories); + addSortedStringArray(generator, DISPLAY_CATEGORIES_KEY, endpointAttributes.displayCategories); /// Additional Attributes Object. if (endpointAttributes.additionalAttributes.hasValue()) { @@ -301,7 +349,7 @@ std::string getEndpointConfigJson( } connectionsJsons.push_back(connectionJsonGenerator.toString()); } - generator.addMembersArray(CONNECTIONS_KEY, connectionsJsons); + addSortedMembersArray(generator, CONNECTIONS_KEY, connectionsJsons); } /// Cookie Object. @@ -320,7 +368,7 @@ std::string getEndpointConfigJson( capabilityConfigJsons.push_back(capabilityConfigJson); } } - generator.addMembersArray(CAPABILITIES_KEY, capabilityConfigJsons); + addSortedMembersArray(generator, CAPABILITIES_KEY, capabilityConfigJsons); return generator.toString(); } diff --git a/CapabilitiesDelegate/test/Storage/SQLiteCapabilitiesDelegateStorageTest.cpp b/CapabilitiesDelegate/test/Storage/SQLiteCapabilitiesDelegateStorageTest.cpp index fbecaab2d9..dd98db7d52 100644 --- a/CapabilitiesDelegate/test/Storage/SQLiteCapabilitiesDelegateStorageTest.cpp +++ b/CapabilitiesDelegate/test/Storage/SQLiteCapabilitiesDelegateStorageTest.cpp @@ -22,6 +22,7 @@ #include #include +#include namespace alexaClientSDK { namespace capabilitiesDelegate { @@ -30,6 +31,7 @@ namespace test { using namespace avsCommon::utils::configuration; using namespace ::testing; +using namespace alexaClientSDK::storage::sqliteStorage; /// Test database file name. static const std::string TEST_DATABASE_FILE_NAME = "SQLiteCapabilitiesDelegateStorageTest.db"; @@ -44,6 +46,9 @@ static const std::string CAPABILITIES_DELEGATE_JSON = R"( )"; // clang-format on +/// The name of the capabilitiesDelegate table. +static const std::string ENDPOINT_CONFIG_TABLE_NAME = "endpointConfigTable"; + /// Test endpoint ID. static const std::string TEST_ENDPOINT_ID_1 = "EndpointID1"; @@ -79,6 +84,9 @@ class SQLiteCapabilitiesDelegateStorageTest : public Test { void TearDown() override; protected: + /// Utility method to create and validate a fresh database. + void setupDatabase(); + /// Cleanup function to close. void closeAndDeleteDB(); @@ -96,6 +104,13 @@ void SQLiteCapabilitiesDelegateStorageTest::closeAndDeleteDB() { } } +void SQLiteCapabilitiesDelegateStorageTest::setupDatabase() { + m_db = SQLiteCapabilitiesDelegateStorage::create(ConfigurationNode::getRoot()); + + ASSERT_THAT(m_db, NotNull()); + ASSERT_TRUE(m_db->createDatabase()); +} + void SQLiteCapabilitiesDelegateStorageTest::SetUp() { /// Initialize Global ConfigurationNode with valid value. auto json = std::shared_ptr(new std::stringstream()); @@ -103,11 +118,6 @@ void SQLiteCapabilitiesDelegateStorageTest::SetUp() { std::vector> jsonStream; jsonStream.push_back(json); ASSERT_TRUE(ConfigurationNode::initialize(jsonStream)); - - m_db = SQLiteCapabilitiesDelegateStorage::create(ConfigurationNode::getRoot()); - - ASSERT_THAT(m_db, NotNull()); - ASSERT_TRUE(m_db->createDatabase()); } void SQLiteCapabilitiesDelegateStorageTest::TearDown() { @@ -137,6 +147,7 @@ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_createValidConfigurationRoot) * Tests if create existing database fails. */ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_createExistingDatabaseFails) { + setupDatabase(); ASSERT_FALSE(m_db->createDatabase()); } @@ -144,14 +155,37 @@ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_createExistingDatabaseFails) * Tests if the open existing database succeeds. */ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_openExistingDatabaseSucceeds) { + setupDatabase(); m_db->close(); ASSERT_TRUE(m_db->open()); } +/** + * Tests if open succeeds when there is an empty database with no Endpoint Config Table. + */ +TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_databaseWithNoEndpointConfigTable) { + /// Create empty database with no tables. + auto sqliteDB = std::unique_ptr( + new alexaClientSDK::storage::sqliteStorage::SQLiteDatabase(TEST_DATABASE_FILE_NAME)); + ASSERT_THAT(sqliteDB, NotNull()); + ASSERT_TRUE(sqliteDB->initialize()); + + /// Open the same database as the SQLiteCapabilitiesDelegateStorage + m_db = SQLiteCapabilitiesDelegateStorage::create(ConfigurationNode::getRoot()); + + ASSERT_FALSE(sqliteDB->tableExists(ENDPOINT_CONFIG_TABLE_NAME)); + ASSERT_THAT(m_db, NotNull()); + ASSERT_TRUE(m_db->open()); + ASSERT_TRUE(sqliteDB->tableExists(ENDPOINT_CONFIG_TABLE_NAME)); + + sqliteDB->close(); +} + /** * Tests if the store method works with a single endpoint. */ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_storeForEndpointWorks) { + setupDatabase(); ASSERT_TRUE(m_db->store(TEST_ENDPOINT_ID_1, TEST_ENDPOINT_CONFIG_1)); std::string testString; ASSERT_TRUE(m_db->load(TEST_ENDPOINT_ID_1, &testString)); @@ -163,6 +197,7 @@ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_storeForEndpointWorks) { * Tests if the store method works with an endpoint map. */ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_storeForEndpointMapWorks) { + setupDatabase(); std::unordered_map storeMap; storeMap.insert({TEST_ENDPOINT_ID_1, TEST_ENDPOINT_CONFIG_1}); storeMap.insert({TEST_ENDPOINT_ID_2, TEST_ENDPOINT_CONFIG_2}); @@ -186,6 +221,7 @@ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_storeForEndpointMapWorks) { * Test if storing an existing entry in to the database replaces the previous value. */ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_storeForExistingEntry) { + setupDatabase(); std::string storedValue; std::unordered_map storeMap; storeMap.insert({TEST_ENDPOINT_ID_1, TEST_ENDPOINT_CONFIG_1}); @@ -204,6 +240,7 @@ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_storeForExistingEntry) { * Test if the load method with endpointId input works. */ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_loadForEndpointWorks) { + setupDatabase(); std::unordered_map storeMap; storeMap.insert({TEST_ENDPOINT_ID_1, TEST_ENDPOINT_CONFIG_1}); storeMap.insert({TEST_ENDPOINT_ID_2, TEST_ENDPOINT_CONFIG_2}); @@ -221,6 +258,7 @@ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_loadForEndpointWorks) { * Test if the load method with endpointId input works. */ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_loadForNonExistingEndpoint) { + setupDatabase(); std::unordered_map storeMap; storeMap.insert({TEST_ENDPOINT_ID_2, TEST_ENDPOINT_CONFIG_2}); ASSERT_TRUE(m_db->store(storeMap)); @@ -233,6 +271,7 @@ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_loadForNonExistingEndpoint) { * Test if the erase method works for a given endpointId. */ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_eraseWorks) { + setupDatabase(); std::unordered_map storeMap; storeMap.insert({TEST_ENDPOINT_ID_1, TEST_ENDPOINT_CONFIG_1}); storeMap.insert({TEST_ENDPOINT_ID_2, TEST_ENDPOINT_CONFIG_2}); @@ -253,6 +292,7 @@ TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_eraseWorks) { } TEST_F(SQLiteCapabilitiesDelegateStorageTest, test_clearDatabaseWorks) { + setupDatabase(); /// Store one item in the database. std::unordered_map testMap; ASSERT_TRUE(m_db->store(TEST_ENDPOINT_ID_1, TEST_ENDPOINT_CONFIG_1)); diff --git a/CapabilitiesDelegate/test/Utils/DiscoveryUtilsTest.cpp b/CapabilitiesDelegate/test/Utils/DiscoveryUtilsTest.cpp index a592db653e..018692c4b2 100644 --- a/CapabilitiesDelegate/test/Utils/DiscoveryUtilsTest.cpp +++ b/CapabilitiesDelegate/test/Utils/DiscoveryUtilsTest.cpp @@ -41,25 +41,35 @@ using namespace rapidjson; using Properties = CapabilityConfiguration::Properties; /// A Test CapabilityConfiguration type. -static const std::string TEST_TYPE = "TEST_TYPE"; +static const std::string TEST_TYPE_1 = "TEST_TYPE_1"; /// A Test CapabilityConfiguration interface name. -static const std::string TEST_INTERFACE_NAME = "TEST_INTERFACE_NAME"; +static const std::string TEST_INTERFACE_NAME_1 = "TEST_INTERFACE_NAME_1"; /// A Test CapabilityConfiguration version. -static const std::string TEST_VERSION = "TEST_VERSION"; +static const std::string TEST_VERSION_1 = "TEST_VERSION_1"; + +/// A Test CapabilityConfiguration type. +static const std::string TEST_TYPE_2 = "TEST_TYPE_2"; + +/// A Test CapabilityConfiguration interface name. +static const std::string TEST_INTERFACE_NAME_2 = "TEST_INTERFACE_NAME_2"; + +/// A Test CapabilityConfiguration version. +static const std::string TEST_VERSION_2 = "TEST_VERSION_2"; /// A Test CapabilityConfiguration instance name. static const Optional TEST_INSTANCE_NAME("TEST_INSTANCE_NAME"); /// A Test CapabilityConfiguration properties supportedList. -static const std::vector TEST_SUPPORTED_LIST{"TEST_PROPERTY"}; +static const std::vector TEST_SUPPORTED_LIST{"TEST_PROPERTY_1", "TEST_PROPERTY_2"}; /// A Test CapabilityConfiguration Optional properties. static const Optional TEST_PROPERTIES = Properties(false, false, TEST_SUPPORTED_LIST); +/// A Test CapabilityConfiguration supported properties string. static const std::string EXPECTED_TEST_PROPERTIES_STRING = - R"({"supported":[{"name":"TEST_PROPERTY"}],"proactivelyReported":false,"retrievable":false})"; + R"({"supported":[{"name":"TEST_PROPERTY_1"},{"name":"TEST_PROPERTY_2"}],"proactivelyReported":false,"retrievable":false})"; /// A Test valid JSON value. static const std::string TEST_VALID_CONFIG_JSON = R"({"key":{"key1":"value1"}})"; @@ -81,7 +91,7 @@ static const std::string TEST_DESCRIPTION = "TEST_DESCRIPTION"; static const std::string TEST_MANUFACTURER_NAME = "TEST_MANUFACTURER_NAME"; /// A Test display category list. -static const std::vector TEST_DISPLAY_CATEGORIES = {"TEST_DISPLAY_CATEGORY"}; +static const std::vector TEST_DISPLAY_CATEGORIES = {"TEST_DISPLAY_CATEGORY_1", "TEST_DISPLAY_CATEGORY_2"}; /// A Test customer identifier. static const std::string TEST_CUSTOMER_IDENTIFIER = "TEST_CUSTOMER_IDENTIFIER"; @@ -163,9 +173,9 @@ AVSDiscoveryEndpointAttributes getTestEndpointAttributes() { endpointAttributes.cookies = cookie; CapabilityConfiguration capability( - TEST_TYPE, - TEST_INTERFACE_NAME, - TEST_VERSION, + TEST_TYPE_1, + TEST_INTERFACE_NAME_1, + TEST_VERSION_1, TEST_INSTANCE_NAME, TEST_PROPERTIES, TEST_ADDITIONAL_CONFIGURATIONS); @@ -174,25 +184,6 @@ AVSDiscoveryEndpointAttributes getTestEndpointAttributes() { return endpointAttributes; } -/** - * Returns an array of @c CapabilityConfigurations to be used in unit tests. - * - * @return an array of @c CapabilityConfigurations. - */ -std::vector getTestCapabilities() { - std::vector capabilities; - CapabilityConfiguration capability( - TEST_TYPE, - TEST_INTERFACE_NAME, - TEST_VERSION, - TEST_INSTANCE_NAME, - TEST_PROPERTIES, - TEST_ADDITIONAL_CONFIGURATIONS); - capabilities.push_back(capability); - - return capabilities; -} - /** * Creates an @c AVSDiscoveryEndpointAttributes structure using the parameters passed in. * @param endpointId The endpoint ID string. @@ -330,14 +321,18 @@ void validateEndpointConfigJson(const std::string& endpointConfigJson) { capabilitiesMaps.push_back(capabilitiesMap); } - ASSERT_EQ(capabilitiesMaps.size(), 1U); + ASSERT_EQ(capabilitiesMaps.size(), 2U); - ASSERT_EQ(capabilitiesMaps[0]["interface"], TEST_INTERFACE_NAME); - ASSERT_EQ(capabilitiesMaps[0]["version"], TEST_VERSION); - ASSERT_EQ(capabilitiesMaps[0]["type"], TEST_TYPE); + ASSERT_EQ(capabilitiesMaps[0]["interface"], TEST_INTERFACE_NAME_1); + ASSERT_EQ(capabilitiesMaps[0]["version"], TEST_VERSION_1); + ASSERT_EQ(capabilitiesMaps[0]["type"], TEST_TYPE_1); ASSERT_EQ(capabilitiesMaps[0]["instance"], TEST_INSTANCE_NAME.value()); ASSERT_EQ(capabilitiesMaps[0]["properties"], EXPECTED_TEST_PROPERTIES_STRING); ASSERT_EQ(capabilitiesMaps[0]["configuration"], TEST_VALID_CONFIG_JSON); + + ASSERT_EQ(capabilitiesMaps[1]["interface"], TEST_INTERFACE_NAME_2); + ASSERT_EQ(capabilitiesMaps[1]["version"], TEST_VERSION_2); + ASSERT_EQ(capabilitiesMaps[1]["type"], TEST_TYPE_2); } /** @@ -422,26 +417,26 @@ class DiscoveryUtilsTest : public Test {}; */ TEST_F(DiscoveryUtilsTest, test_validateCapabilityConfiguration) { /// Invalid Type - ASSERT_FALSE(validateCapabilityConfiguration(CapabilityConfiguration("", TEST_INTERFACE_NAME, TEST_VERSION))); + ASSERT_FALSE(validateCapabilityConfiguration(CapabilityConfiguration("", TEST_INTERFACE_NAME_1, TEST_VERSION_1))); /// Invalid Interface Name - ASSERT_FALSE(validateCapabilityConfiguration(CapabilityConfiguration(TEST_TYPE, "", TEST_VERSION))); + ASSERT_FALSE(validateCapabilityConfiguration(CapabilityConfiguration(TEST_TYPE_1, "", TEST_VERSION_1))); /// Invalid Version - ASSERT_FALSE(validateCapabilityConfiguration(CapabilityConfiguration(TEST_TYPE, TEST_INTERFACE_NAME, ""))); + ASSERT_FALSE(validateCapabilityConfiguration(CapabilityConfiguration(TEST_TYPE_1, TEST_INTERFACE_NAME_1, ""))); /// Invalid instance name Optional invalidInstanceName(""); ASSERT_FALSE(validateCapabilityConfiguration( - CapabilityConfiguration(TEST_TYPE, TEST_INTERFACE_NAME, TEST_VERSION, invalidInstanceName))); + CapabilityConfiguration(TEST_TYPE_1, TEST_INTERFACE_NAME_1, TEST_VERSION_1, invalidInstanceName))); /// Invalid supported list std::vector supportedList; Properties invalidSupportedListProperties(false, false, supportedList); ASSERT_FALSE(validateCapabilityConfiguration(CapabilityConfiguration( - TEST_TYPE, - TEST_INTERFACE_NAME, - TEST_VERSION, + TEST_TYPE_1, + TEST_INTERFACE_NAME_1, + TEST_VERSION_1, TEST_INSTANCE_NAME, Optional(invalidSupportedListProperties)))); @@ -449,13 +444,18 @@ TEST_F(DiscoveryUtilsTest, test_validateCapabilityConfiguration) { CapabilityConfiguration::AdditionalConfigurations additionalConfigurations; additionalConfigurations.insert({"TEST", "abc:"}); ASSERT_FALSE(validateCapabilityConfiguration(CapabilityConfiguration( - TEST_TYPE, TEST_INTERFACE_NAME, TEST_VERSION, TEST_INSTANCE_NAME, TEST_PROPERTIES, additionalConfigurations))); + TEST_TYPE_1, + TEST_INTERFACE_NAME_1, + TEST_VERSION_1, + TEST_INSTANCE_NAME, + TEST_PROPERTIES, + additionalConfigurations))); /// Valid case with fully loaded @c CapabilityConfiguration. ASSERT_TRUE(validateCapabilityConfiguration(CapabilityConfiguration( - TEST_TYPE, - TEST_INTERFACE_NAME, - TEST_VERSION, + TEST_TYPE_1, + TEST_INTERFACE_NAME_1, + TEST_VERSION_1, TEST_INSTANCE_NAME, TEST_PROPERTIES, TEST_ADDITIONAL_CONFIGURATIONS))); @@ -490,7 +490,29 @@ TEST_F(DiscoveryUtilsTest, test_validateAVSDiscoveryEndpointAttributes) { * Test if the getEndpointConfigJson method works as expected. */ TEST_F(DiscoveryUtilsTest, test_formatEndpointConfigJson) { - validateEndpointConfigJson(getEndpointConfigJson(getTestEndpointAttributes(), getTestCapabilities())); + std::vector capabilities; + + CapabilityConfiguration capability1( + TEST_TYPE_1, + TEST_INTERFACE_NAME_1, + TEST_VERSION_1, + TEST_INSTANCE_NAME, + TEST_PROPERTIES, + TEST_ADDITIONAL_CONFIGURATIONS); + + CapabilityConfiguration capability2(TEST_TYPE_2, TEST_INTERFACE_NAME_2, TEST_VERSION_2); + + capabilities.push_back(capability1); + capabilities.push_back(capability2); + + validateEndpointConfigJson(getEndpointConfigJson(getTestEndpointAttributes(), capabilities)); + + /// Check if endpoint configuration is valid even after flipping the contents of the capabilities array. + capabilities.clear(); + capabilities.push_back(capability2); + capabilities.push_back(capability1); + + validateEndpointConfigJson(getEndpointConfigJson(getTestEndpointAttributes(), capabilities)); } /** @@ -518,6 +540,31 @@ TEST_F(DiscoveryUtilsTest, test_deleteReportEvent) { validateDiscoveryEvent(event, DELETE_REPORT_EVENT_NAME, TEST_AUTH_TOKEN, {TEST_ENDPOINT_ID}); } +TEST_F(DiscoveryUtilsTest, test_compareEndpointConfigJsons) { + std::string endpointConfig1, endpointConfig2; + + /// Contents of the JSON are same. + endpointConfig1 = + R"({"endpointId":"a", "friendlyName":"c", "capabilities":[{"interfaceName":"abc", "version":"123"}, {"interfaceName":"xyz", "version":"456"}]})"; + endpointConfig2 = + R"({"endpointId":"a", "friendlyName":"c", "capabilities":[{"interfaceName":"abc", "version":"123"}, {"interfaceName":"xyz", "version":"456"}]})"; + ASSERT_TRUE(compareEndpointConfigurations(endpointConfig1, endpointConfig2)); + + /// Elements out of order + endpointConfig1 = + R"({"endpointId":"a", "friendlyName":"c", "capabilities":[{"interfaceName":"abc", "version":"123"}, {"interfaceName":"xyz", "version":"456"}]})"; + endpointConfig2 = + R"({"capabilities":[{"interfaceName":"abc", "version":"123"}, {"interfaceName":"xyz", "version":"456"}], "friendlyName":"c", "endpointId":"a"})"; + ASSERT_TRUE(compareEndpointConfigurations(endpointConfig1, endpointConfig2)); + + /// Order of arrays doesn't match + endpointConfig1 = + R"({"endpointId":"a", "friendlyName":"c", "capabilities":[{"interfaceName":"abc", "version":"123"}, {"interfaceName":"xyz", "version":"456"}]})"; + endpointConfig2 = + R"({"endpointId":"a", "friendlyName":"c", "capabilities":[{"interfaceName":"xyz", "version":"456"}, {"interfaceName":"abc", "version":"123"}]})"; + ASSERT_FALSE(compareEndpointConfigurations(endpointConfig1, endpointConfig2)); +} + } // namespace test } // namespace utils } // namespace capabilitiesDelegate diff --git a/CapabilityAgents/AIP/include/AIP/AudioInputProcessor.h b/CapabilityAgents/AIP/include/AIP/AudioInputProcessor.h index e8df5f206c..4f5fe55f5b 100644 --- a/CapabilityAgents/AIP/include/AIP/AudioInputProcessor.h +++ b/CapabilityAgents/AIP/include/AIP/AudioInputProcessor.h @@ -779,6 +779,11 @@ class AudioInputProcessor * passed to the directive sequencer. */ std::string m_preCachedDialogRequestId; + + /** + * Value that will contain the time since last wake from suspend when AIP acquires the wakelock. + */ + std::chrono::milliseconds m_timeSinceLastResumeMS; }; } // namespace aip diff --git a/CapabilityAgents/AIP/src/AudioInputProcessor.cpp b/CapabilityAgents/AIP/src/AudioInputProcessor.cpp index ca5a7962b5..4f9acc8916 100644 --- a/CapabilityAgents/AIP/src/AudioInputProcessor.cpp +++ b/CapabilityAgents/AIP/src/AudioInputProcessor.cpp @@ -215,35 +215,6 @@ static const std::chrono::milliseconds PREROLL_DURATION = std::chrono::milliseco static const int MILLISECONDS_PER_SECOND = 1000; -/** - * Handles a Metric event by creating and recording it. Failure to create or record the event results - * in an early return. - * - * @param metricRecorder The @c MetricRecorderInterface which records Metric events. - * @param activityName The activityName of the Metric event. - * @param dataPoint The @c DataPoint of this Metric event (e.g. duration). - * @param dialogRequestId The dialogRequestId associated with this Metric event; default is empty string. - */ -static void submitMetric( - const std::shared_ptr& metricRecorder, - const std::string& activityName, - const DataPoint& dataPoint, - const std::string& dialogRequestId = "") { - auto metricEventBuilder = - MetricEventBuilder{} - .setActivityName(activityName) - .addDataPoint(dataPoint) - .addDataPoint(DataPointStringBuilder{}.setName("DIALOG_REQUEST_ID").setValue(dialogRequestId).build()); - - auto metricEvent = metricEventBuilder.build(); - - if (metricEvent == nullptr) { - ACSDK_ERROR(LX("Error creating metric with explicit dialogRequestId")); - return; - } - recordMetric(metricRecorder, metricEvent); -} - /** * Handles a Metric event by creating and recording it. Failure to create or record the event results * in an early return. @@ -602,7 +573,8 @@ AudioInputProcessor::AudioInputProcessor( m_speechConfirmation{speechConfirmation}, m_wakeWordsSetting{wakeWordsSetting}, m_powerResourceManager{powerResourceManager}, - m_expectSpeechTimeoutHandler{expectSpeechTimeoutHandler} { + m_expectSpeechTimeoutHandler{expectSpeechTimeoutHandler}, + m_timeSinceLastResumeMS{std::chrono::milliseconds(0)} { m_capabilityConfigurations.insert(capabilitiesConfiguration); } @@ -717,13 +689,14 @@ void AudioInputProcessor::handleSetEndOfSpeechOffsetDirective(std::shared_ptrdirective->getPayload(); int64_t endOfSpeechOffset = 0; - int64_t startOfSpeechTimestamp = 0; + std::string startOfSpeechTimeStampInString; bool foundEnd = json::jsonUtils::retrieveValue(payload, END_OF_SPEECH_OFFSET_FIELD_NAME, &endOfSpeechOffset); bool foundStart = json::jsonUtils::retrieveValue(payload, START_OF_SPEECH_TIMESTAMP_FIELD_NAME, &startOfSpeechTimeStampInString); if (foundEnd && foundStart) { + int64_t startOfSpeechTimestamp = 0; auto offset = milliseconds(endOfSpeechOffset); submitMetric( m_metricRecorder, @@ -1002,8 +975,13 @@ bool AudioInputProcessor::executeRecognize( // Handle metrics for this event. submitMetric( m_metricRecorder, - START_OF_UTTERANCE_ACTIVITY_NAME, - DataPointCounterBuilder{}.setName(START_OF_UTTERANCE).increment(1).build(), + MetricEventBuilder{} + .setActivityName(START_OF_UTTERANCE_ACTIVITY_NAME) + .addDataPoint(DataPointStringBuilder{} + .setName("TIME_SINCE_RESUME_ID") + .setValue(std::to_string(m_timeSinceLastResumeMS.count())) + .build()) + .addDataPoint(DataPointCounterBuilder{}.setName(START_OF_UTTERANCE).increment(1).build()), m_preCachedDialogRequestId); if (initiatedByWakeword) { @@ -1123,7 +1101,6 @@ bool AudioInputProcessor::executeStopCapture(bool stopImmediately, std::shared_p return true; } if (m_state != ObserverInterface::State::RECOGNIZING) { - static const char* errorMessage = "StopCapture only allowed in RECOGNIZING state."; auto returnValue = false; if (info) { if (info->result) { @@ -1136,6 +1113,7 @@ bool AudioInputProcessor::executeStopCapture(bool stopImmediately, std::shared_p .m("StopCapture directive ignored because local StopCapture was performed.")); } else { + static const char* errorMessage = "StopCapture only allowed in RECOGNIZING state."; info->result->setFailed(errorMessage); ACSDK_ERROR(LX("executeStopCaptureFailed") .d("reason", "invalidState") @@ -1228,8 +1206,8 @@ bool AudioInputProcessor::executeExpectSpeech(milliseconds timeout, std::shared_ return true; } if (m_state != ObserverInterface::State::IDLE && m_state != ObserverInterface::State::BUSY) { - static const char* errorMessage = "ExpectSpeech only allowed in IDLE or BUSY state."; if (info->result) { + static const char* errorMessage = "ExpectSpeech only allowed in IDLE or BUSY state."; info->result->setFailed(errorMessage); } removeDirective(info); @@ -1531,6 +1509,7 @@ void AudioInputProcessor::managePowerResource(ObserverInterface::State newState) case ObserverInterface::State::EXPECTING_SPEECH: m_powerResourceManager->acquirePowerResource( POWER_RESOURCE_COMPONENT_NAME, PowerResourceManagerInterface::PowerResourceLevel::ACTIVE_HIGH); + m_timeSinceLastResumeMS = m_powerResourceManager->getTimeSinceLastResumeMS(); break; case ObserverInterface::State::BUSY: case ObserverInterface::State::IDLE: diff --git a/CapabilityAgents/RangeController/src/RangeControllerCapabilityAgent.cpp b/CapabilityAgents/RangeController/src/RangeControllerCapabilityAgent.cpp index 2bb3c5295a..d885c0fc63 100644 --- a/CapabilityAgents/RangeController/src/RangeControllerCapabilityAgent.cpp +++ b/CapabilityAgents/RangeController/src/RangeControllerCapabilityAgent.cpp @@ -296,7 +296,6 @@ void RangeControllerCapabilityAgent::handleDirective(std::shared_ptr result; if (directiveName == NAME_SETRANGEVALUE) { executeSetRangeValueDirective(info, payload); } else if (directiveName == NAME_ADJUSTRANGEVALUE) { diff --git a/CapabilityAgents/SpeakerManager/include/SpeakerManager/ChannelVolumeManager.h b/CapabilityAgents/SpeakerManager/include/SpeakerManager/ChannelVolumeManager.h index a73b6287aa..9f670118b1 100644 --- a/CapabilityAgents/SpeakerManager/include/SpeakerManager/ChannelVolumeManager.h +++ b/CapabilityAgents/SpeakerManager/include/SpeakerManager/ChannelVolumeManager.h @@ -46,6 +46,7 @@ class ChannelVolumeManager : public avsCommon::sdkInterfaces::ChannelVolumeInter /// ChannelVolumeInterface Functions. /// @{ avsCommon::sdkInterfaces::ChannelVolumeInterface::Type getSpeakerType() const override; + size_t getId() const override; bool startDucking() override; bool stopDucking() override; bool setUnduckedVolume(int8_t volume) override; diff --git a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManager.h b/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManager.h index a50f5f8bcd..255f4656b1 100644 --- a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManager.h +++ b/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManager.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -47,9 +48,10 @@ namespace speakerManager { /** * This class implements a @c CapabilityAgent that handles the AVS @c Speaker API. * - * The @c SpeakerManager can handle multiple @c ChannelVolumeInterface objects. @c ChannelVolumeInterface - * are grouped by their respective @c ChannelVolumeInterface::Type, and the volume and mute state will be consistent - * across each type. For example, to change the volume of all @c ChannelVolumeInterface objects of a specific type: + * The @c SpeakerManager can handle multiple @c ChannelVolumeInterface objects and dedupe them with + * the same getId() value. The @c ChannelVolumeInterface are grouped by their respective + * @c ChannelVolumeInterface::Type, and the volume and mute state will be consistent across each type. + * For example, to change the volume of all @c ChannelVolumeInterface objects of a specific type: * * @code{.cpp} * // Use local setVolume API. @@ -197,6 +199,50 @@ class SpeakerManager const int minUnmuteVolume, std::shared_ptr metricRecorder); + /// Hash functor to use identifier of @c ChannelVolumeInterface as the key in SpeakerSet. + struct ChannelVolumeInterfaceHash { + public: + size_t operator()(const std::shared_ptr& key) const { + if (nullptr == key) { + /// This should never happen because the only way to add a ChannelVolumeInterface into the SpeakerSet + /// has a guard of nullptr. + return 0; + } + return key->getId(); + } + }; + + /// Comparator to compare two shared_ptrs of @c ChannelVolumeInterface objects in SpeakerSet. + struct ChannelVolumeInterfaceEqualTo { + public: + bool operator()( + const std::shared_ptr channelVolumeInterface1, + std::shared_ptr channelVolumeInterface2) const { + if (!channelVolumeInterface1 || !channelVolumeInterface2) { + /// This should never happen because the only way to add a ChannelVolumeInterface into the SpeakerSet + /// has a guard of nullptr. + return true; + } + return channelVolumeInterface1->getId() == channelVolumeInterface2->getId(); + } + }; + + /// Alias for a set of @c ChannelVolumeInterface keyed by the id. + using SpeakerSet = std::unordered_set< + std::shared_ptr, + ChannelVolumeInterfaceHash, + ChannelVolumeInterfaceEqualTo>; + + /** + * Internal function to add @c ChannelVolumeInterface object into SpeakerMap. + * Invalid element(nullptr or ChannelVolumeInterface with the same getId() value) is not allowed to be added into + * the map. + * + * @param channelVolumeInterface The @c ChannelVolumeInterface object. + */ + void addChannelVolumeInterfaceIntoSpeakerMap( + std::shared_ptr channelVolumeInterface); + /** * Parses the payload from a string into a rapidjson document. * @@ -397,10 +443,13 @@ class SpeakerManager /// the @c volume to restore to when unmuting at 0 volume const int m_minUnmuteVolume; - /// A multimap contain ChannelVolumeInterfaces keyed by @c Type. - std::multimap< + /// An unordered_map contains ChannelVolumeInterfaces keyed by @c Type. Only internal function + /// addChannelVolumeInterfaceIntoSpeakerMap can insert an element into this map to ensure that no invalid element + /// is added. The @c ChannelVolumeInterface in the map is deduped by the getId() value. + std::unordered_map< avsCommon::sdkInterfaces::ChannelVolumeInterface::Type, - std::shared_ptr> + SpeakerSet, + avsCommon::utils::functional::EnumClassHash> m_speakerMap; /// The observers to be notified whenever any of the @c SpeakerSetting changing APIs are called. diff --git a/CapabilityAgents/SpeakerManager/src/ChannelVolumeManager.cpp b/CapabilityAgents/SpeakerManager/src/ChannelVolumeManager.cpp index d016e38abe..e70f842d09 100644 --- a/CapabilityAgents/SpeakerManager/src/ChannelVolumeManager.cpp +++ b/CapabilityAgents/SpeakerManager/src/ChannelVolumeManager.cpp @@ -89,6 +89,10 @@ ChannelVolumeInterface::Type ChannelVolumeManager::getSpeakerType() const { return m_type; } +size_t ChannelVolumeManager::getId() const { + return (size_t)m_speaker.get(); +} + bool ChannelVolumeManager::startDucking() { std::lock_guard locker{m_mutex}; ACSDK_DEBUG5(LX(__func__)); diff --git a/CapabilityAgents/SpeakerManager/src/SpeakerManager.cpp b/CapabilityAgents/SpeakerManager/src/SpeakerManager.cpp index c9cdc0175a..0a1c122ece 100644 --- a/CapabilityAgents/SpeakerManager/src/SpeakerManager.cpp +++ b/CapabilityAgents/SpeakerManager/src/SpeakerManager.cpp @@ -206,25 +206,40 @@ SpeakerManager::SpeakerManager( m_retryTimer{DEFAULT_RETRY_TABLE}, m_maxRetries{DEFAULT_RETRY_TABLE.size()}, m_maximumVolumeLimit{AVS_SET_VOLUME_MAX} { - for (auto groupVolume : groupVolumeInterfaces) { - m_speakerMap.insert(std::pair>( - groupVolume->getSpeakerType(), groupVolume)); + for (auto& groupVolume : groupVolumeInterfaces) { + addChannelVolumeInterfaceIntoSpeakerMap(groupVolume); } + m_capabilityConfigurations.insert(getSpeakerCapabilityConfiguration()); +} - ACSDK_INFO(LX("mapCreated") - .d("numSpeakerVolume", m_speakerMap.count(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)) - .d("numAlertsVolume", m_speakerMap.count(ChannelVolumeInterface::Type::AVS_ALERTS_VOLUME))); +void SpeakerManager::addChannelVolumeInterfaceIntoSpeakerMap( + std::shared_ptr channelVolumeInterface) { + if (!channelVolumeInterface) { + ACSDK_ERROR(LX("addChannelVolumeInterfaceFailed").d("reason", "nullChannelVolumeInterface")); + return; + } - // If we have at least one AVS_SPEAKER_VOLUME speaker, update the Context initially. - const auto type = ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME; - if (m_speakerMap.count(type)) { - SpeakerInterface::SpeakerSettings settings; - if (!validateSpeakerSettingsConsistency(type, &settings) || !updateContextManager(type, settings)) { - ACSDK_ERROR(LX("initialUpdateContextManagerFailed")); + auto type = channelVolumeInterface->getSpeakerType(); + auto it = m_speakerMap.find(type); + if (m_speakerMap.end() == it) { + // If we have one AVS_SPEAKER_VOLUME speaker, update the Context initially. + SpeakerSet speakerSet; + speakerSet.insert(channelVolumeInterface); + m_speakerMap.insert(std::make_pair(type, speakerSet)); + if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == type) { + SpeakerInterface::SpeakerSettings settings; + if (!validateSpeakerSettingsConsistency(type, &settings) || !updateContextManager(type, settings)) { + ACSDK_ERROR(LX(__func__).m("speakerSettingsInconsistent or initialUpdateContextManagerFailed")); + } + } + } else { + if (it->second.find(channelVolumeInterface) != it->second.end()) { + ACSDK_WARN(LX(__func__).d("type", type).m("Duplicated ChannelVolumeInterface")); + } else { + it->second.insert(channelVolumeInterface); } } - - m_capabilityConfigurations.insert(getSpeakerCapabilityConfiguration()); + ACSDK_DEBUG(LX(__func__).d("type", type).d("sizeOfSpeakerSet", m_speakerMap[type].size())); } std::shared_ptr getSpeakerCapabilityConfiguration() { @@ -502,16 +517,8 @@ void SpeakerManager::removeSpeakerManagerObserver( bool SpeakerManager::validateSpeakerSettingsConsistency( ChannelVolumeInterface::Type type, SpeakerInterface::SpeakerSettings* settings) { - // Iterate through speakers and ensure all have the same volume. - // Returns a pair of iterators. - auto beginIteratorAndEndIterator = m_speakerMap.equal_range(type); - const auto begin = beginIteratorAndEndIterator.first; - const auto end = beginIteratorAndEndIterator.second; - bool consistent = true; - - SpeakerInterface::SpeakerSettings comparator; - - if (begin == end) { + auto it = m_speakerMap.find(type); + if (m_speakerMap.end() == it) { ACSDK_ERROR( LX("validateSpeakerSettingsConsistencyFailed").d("reason", "noSpeakersWithTypeFound").d("type", type)); submitMetric(m_metricRecorder, "noSpeakersWithType", 1); @@ -520,33 +527,31 @@ bool SpeakerManager::validateSpeakerSettingsConsistency( submitMetric(m_metricRecorder, "noSpeakersWithType", 0); // Get settings value to compare the rest against. - if (!begin->second->getSpeakerSettings(&comparator)) { + SpeakerInterface::SpeakerSettings comparator; + auto begin = it->second.begin(); + if (!(*begin)->getSpeakerSettings(&comparator)) { ACSDK_ERROR(LX("validateSpeakerSettingsConsistencyFailed").d("reason", "gettingSpeakerSettingsFailed")); submitMetric(m_metricRecorder, "speakersCannotGetSetting", 1); return false; } - std::multimap>::iterator - typeAndSpeakerIterator = begin; - while (++typeAndSpeakerIterator != end) { - auto volumeGroup = typeAndSpeakerIterator->second; - SpeakerInterface::SpeakerSettings temp; - - // Retrieve speaker settings of current speaker. - if (!volumeGroup->getSpeakerSettings(&temp)) { + // Iterate through speakers and ensure all have the same volume. + bool consistent = true; + while (++begin != it->second.end()) { + SpeakerInterface::SpeakerSettings currentSpeaker; + if (!(*begin)->getSpeakerSettings(¤tSpeaker)) { ACSDK_ERROR(LX("validateSpeakerSettingsConsistencyFailed").d("reason", "gettingSpeakerSettingsFailed")); submitMetric(m_metricRecorder, "speakersCannotGetSetting", 1); return false; } - - if (comparator.volume != temp.volume || comparator.mute != temp.mute) { + if (comparator.volume != currentSpeaker.volume || comparator.mute != currentSpeaker.mute) { submitMetric(m_metricRecorder, "speakersInconsistent", 1); ACSDK_ERROR(LX("validateSpeakerSettingsConsistencyFailed") .d("reason", "inconsistentSpeakerSettings") .d("comparatorVolume", static_cast(comparator.volume)) .d("comparatorMute", comparator.mute) - .d("volume", static_cast(temp.volume)) - .d("mute", temp.mute)); + .d("volume", static_cast(currentSpeaker.volume)) + .d("mute", currentSpeaker.mute)); consistent = false; break; } @@ -631,7 +636,8 @@ bool SpeakerManager::executeSetVolume( int8_t volume, const SpeakerManagerInterface::NotificationProperties& properties) { ACSDK_DEBUG9(LX("executeSetVolumeCalled").d("volume", static_cast(volume))); - if (m_speakerMap.count(type) == 0) { + auto it = m_speakerMap.find(type); + if (m_speakerMap.end() == it) { ACSDK_ERROR(LX("executeSetVolumeFailed").d("reason", "noSpeakersWithType").d("type", type)); submitMetric(m_metricRecorder, "setVolumeFailedZeroSpeakers", 1); return false; @@ -661,19 +667,17 @@ bool SpeakerManager::executeSetVolume( } const int8_t previousVolume = settings.volume; - retryAndApplySettings([this, type, adjustedVolume]() -> bool { + auto begin = it->second.begin(); + auto end = it->second.end(); + retryAndApplySettings([this, adjustedVolume, &begin, end]() -> bool { // Go through list of Speakers with ChannelVolumeInterface::Type equal // to type, and call setVolume. - auto beginIteratorAndEndIterator = m_speakerMap.equal_range(type); - auto begin = beginIteratorAndEndIterator.first; - auto end = beginIteratorAndEndIterator.second; - - for (auto typeAndSpeakerIterator = begin; typeAndSpeakerIterator != end; typeAndSpeakerIterator++) { - auto speaker = typeAndSpeakerIterator->second; - if (!speaker->setUnduckedVolume(adjustedVolume)) { + while (begin != end) { + if (!(*begin)->setUnduckedVolume(adjustedVolume)) { submitMetric(m_metricRecorder, "setSpeakerVolumeFailed", 1); return false; } + begin++; } submitMetric(m_metricRecorder, "setSpeakerVolumeFailed", 0); @@ -686,6 +690,8 @@ bool SpeakerManager::executeSetVolume( return false; } + ACSDK_DEBUG(LX("executeSetVolumeSuccess").d("newVolume", static_cast(settings.volume))); + updateContextManager(type, settings); if (properties.notifyObservers) { @@ -751,8 +757,9 @@ bool SpeakerManager::executeAdjustVolume( ChannelVolumeInterface::Type type, int8_t delta, const SpeakerManagerInterface::NotificationProperties& properties) { - ACSDK_DEBUG9(LX("executeAdjustVolumeCalled").d("delta", (int)delta)); - if (m_speakerMap.count(type) == 0) { + ACSDK_DEBUG9(LX("executeAdjustVolumeCalled").d("delta", static_cast(delta))); + auto it = m_speakerMap.find(type); + if (m_speakerMap.end() == it) { ACSDK_ERROR(LX("executeAdjustVolumeFailed").d("reason", "noSpeakersWithType").d("type", type)); return false; } @@ -776,27 +783,24 @@ bool SpeakerManager::executeAdjustVolume( const int8_t previousVolume = settings.volume; - // calculate resultant volume + // recalculate the delta if needed. if (delta > 0) { - settings.volume = std::min((int)settings.volume + delta, (int)maxVolumeLimit); + delta = std::min(static_cast(settings.volume + delta), static_cast(maxVolumeLimit)) - settings.volume; } else { - settings.volume = std::max((int)settings.volume + delta, (int)AVS_SET_VOLUME_MIN); + delta = + std::max(static_cast(settings.volume + delta), static_cast(AVS_SET_VOLUME_MIN)) - settings.volume; } - retryAndApplySettings([this, type, settings] { + auto begin = it->second.begin(); + auto end = it->second.end(); + retryAndApplySettings([&begin, end, delta] { // Go through list of Speakers with ChannelVolumeInterface::Type equal - // to type, and call setVolume. - // NOTE : we dont use adjustVolume : since if a subset of speakers fail : - // the delta will be reapplied - auto beginIteratorAndEndIterator = m_speakerMap.equal_range(type); - auto begin = beginIteratorAndEndIterator.first; - auto end = beginIteratorAndEndIterator.second; - - for (auto typeAndSpeakerIterator = begin; typeAndSpeakerIterator != end; typeAndSpeakerIterator++) { - auto speaker = typeAndSpeakerIterator->second; - if (!speaker->setUnduckedVolume(settings.volume)) { + // to type, and call adjustUnduckedVolume. + while (begin != end) { + if (!(*begin)->adjustUnduckedVolume(delta)) { return false; } + begin++; } return true; }); @@ -806,7 +810,7 @@ bool SpeakerManager::executeAdjustVolume( return false; } - ACSDK_DEBUG(LX("executeAdjustVolumeSuccess").d("newVolume", (int)settings.volume)); + ACSDK_DEBUG(LX("executeAdjustVolumeSuccess").d("newVolume", static_cast(settings.volume))); updateContextManager(type, settings); @@ -867,22 +871,21 @@ bool SpeakerManager::executeSetMute( } } - if (m_speakerMap.count(type) == 0) { + auto it = m_speakerMap.find(type); + if (m_speakerMap.end() == it) { ACSDK_ERROR(LX("executeSetMuteFailed").d("reason", "noSpeakersWithType").d("type", type)); return false; } - retryAndApplySettings([this, type, mute] { + auto begin = it->second.begin(); + auto end = it->second.end(); + retryAndApplySettings([mute, &begin, end] { // Go through list of Speakers with ChannelVolumeInterface::Type equal to type, and call setMute. - auto beginIteratorAndEndIterator = m_speakerMap.equal_range(type); - auto begin = beginIteratorAndEndIterator.first; - auto end = beginIteratorAndEndIterator.second; - - for (auto typeAndSpeakerIterator = begin; typeAndSpeakerIterator != end; typeAndSpeakerIterator++) { - auto speaker = typeAndSpeakerIterator->second; - if (!speaker->setMute(mute)) { + while (begin != end) { + if (!(*begin)->setMute(mute)) { return false; } + begin++; } return true; }); @@ -896,6 +899,8 @@ bool SpeakerManager::executeSetMute( return false; } + ACSDK_DEBUG(LX("executeSetMuteSuccess").d("mute", mute)); + updateContextManager(type, settings); if (properties.notifyObservers) { @@ -921,11 +926,10 @@ bool SpeakerManager::executeSetMaximumVolumeLimit(const int8_t maximumVolumeLimi ACSDK_DEBUG3(LX(__func__).d("maximumVolumeLimit", static_cast(maximumVolumeLimit))); // First adjust current volumes. - for (auto it = m_speakerMap.begin(); it != m_speakerMap.end(); it = m_speakerMap.upper_bound(it->first)) { - SpeakerInterface::SpeakerSettings speakerSettings; + for (auto it = m_speakerMap.begin(); it != m_speakerMap.end(); it++) { auto speakerType = it->first; ACSDK_DEBUG3(LX(__func__).d("type", speakerType)); - + SpeakerInterface::SpeakerSettings speakerSettings; if (!executeGetSpeakerSettings(speakerType, &speakerSettings)) { ACSDK_ERROR(LX("executeSetMaximumVolumeLimitFailed").d("reason", "getSettingsFailed")); return false; @@ -987,7 +991,7 @@ bool SpeakerManager::executeGetSpeakerSettings( ChannelVolumeInterface::Type type, SpeakerInterface::SpeakerSettings* settings) { ACSDK_DEBUG9(LX("executeGetSpeakerSettingsCalled")); - if (m_speakerMap.count(type) == 0) { + if (m_speakerMap.find(type) == m_speakerMap.end()) { ACSDK_ERROR(LX("executeGetSpeakerSettingsFailed").d("reason", "noSpeakersWithType").d("type", type)); return false; } @@ -1002,22 +1006,7 @@ bool SpeakerManager::executeGetSpeakerSettings( void SpeakerManager::addChannelVolumeInterface( std::shared_ptr channelVolumeInterface) { - if (!channelVolumeInterface) { - ACSDK_ERROR(LX("addChannelVolumeInterfaceFailed").d("reason", "channelVolumeInterface cannot be nullptr")); - return; - } - - m_speakerMap.insert(std::pair>( - channelVolumeInterface->getSpeakerType(), channelVolumeInterface)); - - // If we have at least one AVS_SPEAKER_VOLUME speaker, update the Context initially. - const auto type = ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME; - if (m_speakerMap.count(type) == 1) { - SpeakerInterface::SpeakerSettings settings; - if (!validateSpeakerSettingsConsistency(type, &settings) || !updateContextManager(type, settings)) { - ACSDK_ERROR(LX("initialUpdateContextManagerFailed")); - } - } + addChannelVolumeInterfaceIntoSpeakerMap(channelVolumeInterface); } std::unordered_set> SpeakerManager:: diff --git a/CapabilityAgents/SpeakerManager/test/SpeakerManagerTest.cpp b/CapabilityAgents/SpeakerManager/test/SpeakerManagerTest.cpp index 47d5c114ca..9137e1b2e4 100644 --- a/CapabilityAgents/SpeakerManager/test/SpeakerManagerTest.cpp +++ b/CapabilityAgents/SpeakerManager/test/SpeakerManagerTest.cpp @@ -589,6 +589,24 @@ TEST_F(SpeakerManagerTest, test_getConfiguration) { ASSERT_EQ(configuration[SET_MUTE], audioNonBlockingPolicy); } +/** + * Test that adding duplicated ChannelVolumeInterface instances in the SpeakerManager works correctly. + */ +TEST_F(SpeakerManagerTest, test_addDuplicatedChannelVolumeInterfaces) { + auto channelVolumeInterface = std::make_shared>(); + channelVolumeInterface->DelegateToReal(); + std::vector> channelVolumeInterfaceVec = {channelVolumeInterface, + channelVolumeInterface}; + m_speakerManager = SpeakerManager::create( + channelVolumeInterfaceVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); + EXPECT_CALL(*channelVolumeInterface, setUnduckedVolume(_)).Times(Exactly(1)); + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); + SpeakerManagerInterface::NotificationProperties properties; + std::future future = m_speakerManager->adjustVolume( + ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MAX, properties); + ASSERT_TRUE(future.get()); +} + /** * Test that adding a null observer does not cause any errors in the SpeakerManager. */ @@ -1314,4 +1332,4 @@ TEST_P(SpeakerManagerTest, test_setVolumeDirectiveWhenMuted) { } // namespace test } // namespace speakerManager } // namespace capabilityAgents -} // namespace alexaClientSDK +} // namespace alexaClientSDK \ No newline at end of file diff --git a/CapabilityAgents/System/src/SoftwareInfoSendRequest.cpp b/CapabilityAgents/System/src/SoftwareInfoSendRequest.cpp index 6975b071d2..f06c1b43d9 100644 --- a/CapabilityAgents/System/src/SoftwareInfoSendRequest.cpp +++ b/CapabilityAgents/System/src/SoftwareInfoSendRequest.cpp @@ -101,6 +101,11 @@ void SoftwareInfoSendRequest::onSendCompleted(MessageRequestObserverInterface::S m_observer.reset(); } } else { + if (!m_messageSender) { + ACSDK_ERROR(LX("failedToRetry").d("reason", "alreadyShutdown")); + return; + } + // At each retry, switch the Timer used to specify the time of the next retry. auto& timer = m_retryTimers[m_retryCounter % (sizeof(m_retryTimers) / sizeof(m_retryTimers[0]))]; auto delay = RETRY_TIMER.calculateTimeToRetry(m_retryCounter++); @@ -121,14 +126,16 @@ void SoftwareInfoSendRequest::onExceptionReceived(const std::string& message) { void SoftwareInfoSendRequest::doShutdown() { ACSDK_DEBUG5(LX("doShutdown")); - std::lock_guard lock(m_mutex); + { + std::lock_guard lock(m_mutex); + m_messageSender.reset(); + m_observer.reset(); + } + // Stop timers without locking m_mutex to prevent deadlock for (auto& timer : m_retryTimers) { timer.stop(); } - - m_messageSender.reset(); - m_observer.reset(); } SoftwareInfoSendRequest::SoftwareInfoSendRequest( @@ -145,6 +152,16 @@ SoftwareInfoSendRequest::SoftwareInfoSendRequest( void SoftwareInfoSendRequest::send() { ACSDK_DEBUG5(LX("send").d("firmwareVersion", m_firmwareVersion)); + std::shared_ptr messageSender; + { + std::lock_guard lock(m_mutex); + messageSender = m_messageSender; + if (!messageSender) { + ACSDK_ERROR(LX("failedToRetry").d("reason", "alreadyShutdown")); + return; + } + } + std::string jsonContent; if (!buildJsonForSoftwareInfo(&jsonContent, m_firmwareVersion)) { ACSDK_ERROR(LX("sendFailed").d("reason", "buildJsonForSoftwareInfoFailed")); @@ -153,7 +170,7 @@ void SoftwareInfoSendRequest::send() { } auto request = std::make_shared(jsonContent); request->addObserver(shared_from_this()); - m_messageSender->sendMessage(request); + messageSender->sendMessage(request); } bool SoftwareInfoSendRequest::buildJsonForSoftwareInfo(std::string* jsonContent, FirmwareVersion firmwareVersion) { diff --git a/CapabilityAgents/System/src/SystemCapabilityProvider.cpp b/CapabilityAgents/System/src/SystemCapabilityProvider.cpp index 3cbbf00af5..7722fcae06 100644 --- a/CapabilityAgents/System/src/SystemCapabilityProvider.cpp +++ b/CapabilityAgents/System/src/SystemCapabilityProvider.cpp @@ -44,7 +44,7 @@ static const std::string SYSTEM_CAPABILITY_INTERFACE_TYPE = "AlexaInterface"; /// System interface name static const std::string SYSTEM_CAPABILITY_INTERFACE_NAME = "System"; /// System interface version -static const std::string SYSTEM_CAPABILITY_INTERFACE_VERSION = "2.0"; +static const std::string SYSTEM_CAPABILITY_INTERFACE_VERSION = "2.1"; /// LOCALES_CONFIGURATION_KEY static const std::string LOCALES_CONFIGURATION_KEY = "locales"; diff --git a/CapabilityAgents/System/test/LocaleHandlerTest.cpp b/CapabilityAgents/System/test/LocaleHandlerTest.cpp index b909ab342e..88cb92b678 100644 --- a/CapabilityAgents/System/test/LocaleHandlerTest.cpp +++ b/CapabilityAgents/System/test/LocaleHandlerTest.cpp @@ -105,7 +105,9 @@ class LocaleHandlerTest : public ::testing::Test { void LocaleHandlerTest::SetUp() { auto customerDataManager = std::make_shared(); - m_deviceSettingsManager = std::make_shared(customerDataManager); + DeviceSettingManagerSettingConfigurations configurations; + + m_deviceSettingsManager = std::make_shared(customerDataManager, configurations); m_mockDeviceSettingStorage = std::make_shared(); m_mockExceptionEncounteredSender = std::make_shared(); diff --git a/CapabilityAgents/System/test/StateReportGeneratorTest.cpp b/CapabilityAgents/System/test/StateReportGeneratorTest.cpp index 9f5e4b1c74..bc9c2106a5 100644 --- a/CapabilityAgents/System/test/StateReportGeneratorTest.cpp +++ b/CapabilityAgents/System/test/StateReportGeneratorTest.cpp @@ -87,7 +87,13 @@ void StateReportGeneratorTest::SetUp() { m_mockIntSetting = std::make_shared>(INT_SETTING_VALUE); m_mockStringSetting = std::make_shared>(STRING_SETTING_VALUE); auto customerDataManager = std::make_shared(); - m_mockSettingManager = std::make_shared(customerDataManager); + std::tuple< + SettingConfiguration>, + SettingConfiguration>, + SettingConfiguration>> + settingConfigs; + + m_mockSettingManager = std::make_shared(customerDataManager, settingConfigs); SettingEventMetadata boolSettingMetadata{"test", "", "BoolSettingReport", "boolSetting"}; SettingEventMetadata intSettingMetadata{"test", "", "IntSettingReport", "intSetting"}; diff --git a/Captions/Implementation/src/SystemClockDelay.cpp b/Captions/Implementation/src/SystemClockDelay.cpp index 1a9f67ba03..befa3a09cc 100644 --- a/Captions/Implementation/src/SystemClockDelay.cpp +++ b/Captions/Implementation/src/SystemClockDelay.cpp @@ -22,8 +22,8 @@ namespace alexaClientSDK { namespace captions { void SystemClockDelay::delay(std::chrono::milliseconds milliseconds) { - auto duration = - std::chrono::milliseconds(std::max(std::chrono::milliseconds::zero().count(), milliseconds.count())); + auto duration = std::chrono::milliseconds(std::max( + static_cast(std::chrono::milliseconds::zero().count()), static_cast(milliseconds.count()))); std::this_thread::sleep_for(duration); } diff --git a/Endpoints/src/EndpointBuilder.cpp b/Endpoints/src/EndpointBuilder.cpp index bfd65e238b..13fdfc0ec4 100644 --- a/Endpoints/src/EndpointBuilder.cpp +++ b/Endpoints/src/EndpointBuilder.cpp @@ -559,10 +559,6 @@ std::unique_ptr EndpointBuilder::buildImplementation() { for (auto& capabilityBuilder : m_capabilitiesBuilders) { auto capability = capabilityBuilder(); if (!capability.second) { - if (!m_isDefaultEndpoint) { - ACSDK_ERROR(LX("buildImplementationFailed").d("reason", "buildCapabilityFailed")); - return nullptr; - } endpoint->addCapabilityConfiguration(capability.first); } else { endpoint->addCapability(capability.first, capability.second); diff --git a/Integration/test/AlertsIntegrationTest.cpp b/Integration/test/AlertsIntegrationTest.cpp index eea3509f02..cbe24cdb13 100644 --- a/Integration/test/AlertsIntegrationTest.cpp +++ b/Integration/test/AlertsIntegrationTest.cpp @@ -360,7 +360,9 @@ class AlertsTest : public ::testing::Test { m_customerDataManager = std::make_shared(); - m_deviceSettingsManager = std::make_shared(m_customerDataManager); + DeviceSettingManagerSettingConfigurations configurations; + m_deviceSettingsManager = + std::make_shared(m_customerDataManager, configurations); m_AudioInputProcessor = AudioInputProcessor::create( m_directiveSequencer, diff --git a/Integration/test/AudioInputProcessorIntegrationTest.cpp b/Integration/test/AudioInputProcessorIntegrationTest.cpp index 9e824ba461..e2d10673a1 100644 --- a/Integration/test/AudioInputProcessorIntegrationTest.cpp +++ b/Integration/test/AudioInputProcessorIntegrationTest.cpp @@ -246,9 +246,9 @@ class holdToTalkButton { #if defined(KWD_KITTAI) || defined(KWD_SENSORY) class wakeWordTrigger : public KeyWordObserverInterface { public: - wakeWordTrigger(AudioFormat compatibleAudioFormat, std::shared_ptr aip) { - m_compatibleAudioFormat = compatibleAudioFormat; - m_aip = aip; + wakeWordTrigger(AudioFormat compatibleAudioFormat, std::shared_ptr aip) : + m_compatibleAudioFormat(compatibleAudioFormat), + m_aip(aip) { } void onKeyWordDetected( std::shared_ptr stream, diff --git a/Integration/test/CMakeLists.txt b/Integration/test/CMakeLists.txt index 5d7f01dd75..834194bb88 100644 --- a/Integration/test/CMakeLists.txt +++ b/Integration/test/CMakeLists.txt @@ -112,7 +112,7 @@ if(BUILD_TESTING) if(NETWORK_INTEGRATION_TESTS AND (${CMAKE_SYSTEM_NAME} MATCHES "Linux")) set(networkTestSourceFile - "${CMAKE_CURRENT_SOURCE_DIR}/NetworkIntegrationTests.cpp") + "${CMAKE_CURRENT_SOURCE_DIR}/NetworkIntegrationTest.cpp") get_filename_component(networkTestName ${networkTestSourceFile} NAME_WE) add_executable(${networkTestName} ${networkTestSourceFile}) target_include_directories(${networkTestName} PUBLIC "${INCLUDE_PATH}") diff --git a/Integration/test/NetworkIntegrationTests.cpp b/Integration/test/NetworkIntegrationTest.cpp similarity index 100% rename from Integration/test/NetworkIntegrationTests.cpp rename to Integration/test/NetworkIntegrationTest.cpp diff --git a/KWD/KittAi/src/KittAiKeyWordDetector.cpp b/KWD/KittAi/src/KittAiKeyWordDetector.cpp index 3a92319b87..1b29a717af 100644 --- a/KWD/KittAi/src/KittAiKeyWordDetector.cpp +++ b/KWD/KittAi/src/KittAiKeyWordDetector.cpp @@ -195,10 +195,9 @@ bool KittAiKeyWordDetector::isAudioFormatCompatibleWithKittAi(avsCommon::utils:: void KittAiKeyWordDetector::detectionLoop() { notifyKeyWordDetectorStateObservers(KeyWordDetectorStateObserverInterface::KeyWordDetectorState::ACTIVE); int16_t audioDataToPush[m_maxSamplesPerPush]; - ssize_t wordsRead; while (!m_isShuttingDown) { bool didErrorOccur; - wordsRead = readFromStream( + auto wordsRead = readFromStream( m_streamReader, m_stream, audioDataToPush, m_maxSamplesPerPush, TIMEOUT_FOR_READ_CALLS, &didErrorOccur); if (didErrorOccur) { break; diff --git a/KWD/Sensory/src/SensoryKeywordDetector.cpp b/KWD/Sensory/src/SensoryKeywordDetector.cpp index e609205c9d..851aa0f269 100644 --- a/KWD/Sensory/src/SensoryKeywordDetector.cpp +++ b/KWD/Sensory/src/SensoryKeywordDetector.cpp @@ -309,11 +309,10 @@ void SensoryKeywordDetector::detectionLoop() { m_beginIndexOfStreamReader = m_streamReader->tell(); notifyKeyWordDetectorStateObservers(KeyWordDetectorStateObserverInterface::KeyWordDetectorState::ACTIVE); std::vector audioDataToPush(m_maxSamplesPerPush); - ssize_t wordsRead; SnsrRC result; while (!m_isShuttingDown) { bool didErrorOccur = false; - wordsRead = readFromStream( + auto wordsRead = readFromStream( m_streamReader, m_stream, audioDataToPush.data(), diff --git a/PlaylistParser/include/PlaylistParser/ContentDecrypter.h b/PlaylistParser/include/PlaylistParser/ContentDecrypter.h index e61147f911..dc4c4cc2d7 100644 --- a/PlaylistParser/include/PlaylistParser/ContentDecrypter.h +++ b/PlaylistParser/include/PlaylistParser/ContentDecrypter.h @@ -25,6 +25,7 @@ #include #include +#include "PlaylistParser/Id3TagsRemover.h" #include "PlaylistParser/PlaylistParser.h" namespace alexaClientSDK { @@ -59,13 +60,15 @@ class ContentDecrypter : public avsCommon::utils::RequiresShutdown { * @param key The encryption key. * @param encryptionInfo The @c EncryptionInfo of the encrypted content. * @param streamWriter The writer to write decrypted content. + * @param id3TagRemover A component to remove ID3 tags from content. * @return @c true if decryption and write to stream is successful or @c false otherwise. */ bool decryptAndWrite( const ByteVector& encryptedContent, const ByteVector& key, const avsCommon::utils::playlistParser::EncryptionInfo& encryptionInfo, - std::shared_ptr streamWriter); + const std::shared_ptr& streamWriter, + const std::shared_ptr& id3TagRemover); /** * Converts initialization vector from hex to byte array. diff --git a/PlaylistParser/include/PlaylistParser/Id3TagsRemover.h b/PlaylistParser/include/PlaylistParser/Id3TagsRemover.h new file mode 100644 index 0000000000..9be5d39b6e --- /dev/null +++ b/PlaylistParser/include/PlaylistParser/Id3TagsRemover.h @@ -0,0 +1,113 @@ +/* + * Copyright 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_PLAYLISTPARSER_INCLUDE_PLAYLISTPARSER_ID3TAGSREMOVER_H_ +#define ALEXA_CLIENT_SDK_PLAYLISTPARSER_INCLUDE_PLAYLISTPARSER_ID3TAGSREMOVER_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace alexaClientSDK { +namespace playlistParser { + +/** + * Helper class to remove ID3v2 tags in media content. + */ +class Id3TagsRemover : public avsCommon::utils::RequiresShutdown { +public: + /// Alias for bytes. + using ByteVector = std::vector; + + /** + * Constructor + * + */ + Id3TagsRemover(); + + /// @name RequiresShutdown methods. + /// @{ + void doShutdown() override; + /// @} + + /** + * A function that read from an attachment and remove ID3 tags from the stream and write the stream back to the + * @c streamWriter. + * + * @param attachment The attachment that contains the stream content. + * @param streamWriter The writer to write to the attachment after ID3 tags are removed. + * @return @c true if succeeds and @c false otherwise. + */ + bool removeTagsAndWrite( + const std::shared_ptr& attachment, + const std::shared_ptr& streamWriter); + + /** + * A function that removes any ID3 tags from the buffer. After the call of this function, all ID3 tags in @c + * buffer is removed. If there is no ID3 tag found, then the content in the @c buffer remains the same. + * + * @param[in,out] buffer The buffer in which the ID3 tags will be removed. + */ + void stripID3Tags(ByteVector& buffer); + +private: + /// A struct that is used internally in @c Id3TagsRemover to keep track of states. + struct Context { + /// Buffer that matches partial ID3 tags from last buffer read. + ByteVector remainingBuffer; + + /// Remaining bytes to strip that is part of the ID3 tag. + std::size_t remainingBytesToStrip; + + /// A flag to indicate if the content in the buffer is complete. + bool isBufferComplete; + + /// Constructor + Context() : remainingBytesToStrip{0}, isBufferComplete{false} {}; + }; + + /** + * A function that removes any ID3 tags from the buffer. + * + * @param[in,out] buffer The buffer to remove ID3 tags. + * @param[in,out] context A internally structure to keep track of states. + */ + void stripID3Tags(ByteVector& buffer, Context& context); + + /** + * A helper function to write a buffer to the writer. + * + * @param buffer The buffer to write to the writer. + * @param writer The writer to use to write the buffer to the underlying attachment. + * @return @c true if succeeds and @c false otherwise. + */ + bool writeBufferToWriter( + const ByteVector& buffer, + const std::shared_ptr& writer); + + /// Flag to indicate if a shutdown is occurring. + std::atomic m_shuttingDown; +}; + +} // namespace playlistParser +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_PLAYLISTPARSER_INCLUDE_PLAYLISTPARSER_ID3TAGSREMOVER_H_ diff --git a/PlaylistParser/include/PlaylistParser/UrlContentToAttachmentConverter.h b/PlaylistParser/include/PlaylistParser/UrlContentToAttachmentConverter.h index 4eb6207917..18b792757e 100644 --- a/PlaylistParser/include/PlaylistParser/UrlContentToAttachmentConverter.h +++ b/PlaylistParser/include/PlaylistParser/UrlContentToAttachmentConverter.h @@ -29,6 +29,7 @@ #include #include "PlaylistParser/ContentDecrypter.h" +#include "PlaylistParser/Id3TagsRemover.h" #include "PlaylistParser/PlaylistParser.h" namespace alexaClientSDK { @@ -260,6 +261,9 @@ class UrlContentToAttachmentConverter /// Helper to decrypt encrypted content. std::shared_ptr m_contentDecrypter; + /// Helper to remove ID3 tags from content. + std::shared_ptr m_id3TagsRemover; + /** * @name @c onPlaylistEntryParsed Callback Variables * diff --git a/PlaylistParser/src/CMakeLists.txt b/PlaylistParser/src/CMakeLists.txt index ca966d0f16..7826817892 100644 --- a/PlaylistParser/src/CMakeLists.txt +++ b/PlaylistParser/src/CMakeLists.txt @@ -3,6 +3,7 @@ add_definitions("-DACSDK_LOG_MODULE=PlaylistParser") add_library(PlaylistParser SHARED ContentDecrypter.cpp FFMpegInputBuffer.cpp + Id3TagsRemover.cpp IterativePlaylistParser.cpp M3UParser.cpp PlaylistParser.cpp diff --git a/PlaylistParser/src/ContentDecrypter.cpp b/PlaylistParser/src/ContentDecrypter.cpp index 992098e0b6..03e8d19d96 100644 --- a/PlaylistParser/src/ContentDecrypter.cpp +++ b/PlaylistParser/src/ContentDecrypter.cpp @@ -229,7 +229,13 @@ bool ContentDecrypter::decryptAndWrite( const ByteVector& encryptedContent, const ByteVector& key, const EncryptionInfo& encryptionInfo, - std::shared_ptr streamWriter) { + const std::shared_ptr& streamWriter, + const std::shared_ptr& id3TagRemover) { + if (!id3TagRemover) { + ACSDK_WARN(LX("decryptAndWriteFailed").d("reason", "nullId3TagRemover")); + // fallback to not remove ID3 tags + } + ByteVector ivByteArray; if (!convertIVToByteArray(encryptionInfo.initVector, &ivByteArray)) { ACSDK_ERROR(LX("decryptAndWriteFailed").d("reason", "convertIVToByteArrayFailed")); @@ -264,6 +270,9 @@ bool ContentDecrypter::decryptAndWrite( return false; } + if (id3TagRemover) { + id3TagRemover->stripID3Tags(decryptedContent); + } if (!writeToStream(decryptedContent, streamWriter)) { ACSDK_ERROR(LX("decryptAndWriteFailed").d("reason", "writeFailed")); return false; diff --git a/PlaylistParser/src/Id3TagsRemover.cpp b/PlaylistParser/src/Id3TagsRemover.cpp new file mode 100644 index 0000000000..b8c6c410f2 --- /dev/null +++ b/PlaylistParser/src/Id3TagsRemover.cpp @@ -0,0 +1,244 @@ +/* + * Copyright 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 + +#include "PlaylistParser/Id3TagsRemover.h" + +#include +#include + +namespace alexaClientSDK { +namespace playlistParser { + +using namespace avsCommon::avs::attachment; +using namespace avsCommon::utils::id3Tags; +using namespace avsCommon::utils::sds; + +static const std::string TAG("Id3TagsRemover"); + +/** + * 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) + +/// The number of bytes read from the attachment with each read in the read loop. +static const std::size_t CHUNK_SIZE(1024); + +/// Timeout for polling loops that check activity between read or write. +static const std::chrono::milliseconds WAIT_FOR_ACTIVITY_TIMEOUT{100}; + +Id3TagsRemover::Id3TagsRemover() : RequiresShutdown{"Id3TagsRemover"}, m_shuttingDown{false} { +} + +bool Id3TagsRemover::removeTagsAndWrite( + const std::shared_ptr& attachment, + const std::shared_ptr& streamWriter) { + if (!attachment) { + ACSDK_ERROR(LX("removeTagsAndWriteFailed").d("reason", "nullattachment")); + return false; + } + + if (!streamWriter) { + ACSDK_ERROR(LX("removeTagsAndWriteFailed").d("reason", "nullWriter")); + return false; + } + + auto reader = attachment->createReader(ReaderPolicy::BLOCKING); + if (!reader) { + ACSDK_ERROR(LX("removeTagsAndWriteFailed").d("reason", "nullReader")); + return false; + } + + auto readStatus = AttachmentReader::ReadStatus::OK; + bool streamClosed = false; + Context context; + while (!streamClosed && !m_shuttingDown) { + ByteVector buffer(CHUNK_SIZE, 0); + auto bytesRead = reader->read(buffer.data(), buffer.size(), &readStatus, WAIT_FOR_ACTIVITY_TIMEOUT); + buffer.resize(bytesRead); + + switch (readStatus) { + case AttachmentReader::ReadStatus::CLOSED: + streamClosed = true; + context.isBufferComplete = true; + if (0 == bytesRead && context.remainingBuffer.size() == 0) { + break; + } + /* FALL THROUGH - to add any data received even if closed */ + case AttachmentReader::ReadStatus::OK: + case AttachmentReader::ReadStatus::OK_WOULDBLOCK: + case AttachmentReader::ReadStatus::OK_TIMEDOUT: + stripID3Tags(buffer, context); + if (!writeBufferToWriter(buffer, streamWriter)) { + ACSDK_ERROR(LX("removeTagsAndWriteFailed").d("reason", "writeBufferToWriterFailed")); + return false; + } + break; + case AttachmentReader::ReadStatus::OK_OVERRUN_RESET: + // Current AttachmentReader policy renders this outcome impossible. + ACSDK_ERROR(LX("removeTagsAndWriteFailed").d("reason", readStatus)); + break; + case AttachmentReader::ReadStatus::ERROR_OVERRUN: + case AttachmentReader::ReadStatus::ERROR_BYTES_LESS_THAN_WORD_SIZE: + case AttachmentReader::ReadStatus::ERROR_INTERNAL: + ACSDK_ERROR(LX("removeTagsAndWriteFailed").d("reason", readStatus)); + return false; + } + } + return true; +} + +void Id3TagsRemover::stripID3Tags(ByteVector& buffer) { + Context context; + context.isBufferComplete = true; + stripID3Tags(buffer, context); +} + +void Id3TagsRemover::stripID3Tags(ByteVector& buffer, Context& context) { + // If there were remainingBuffer, prepend them to the buffer + if (context.remainingBuffer.size() > 0) { + ACSDK_DEBUG9(LX("Prepend remaining Buffer") + .d("remainingBuffer", context.remainingBuffer.size()) + .d("buffer", buffer.size())); + context.remainingBuffer.insert(context.remainingBuffer.end(), buffer.begin(), buffer.end()); + buffer.clear(); + buffer.swap(context.remainingBuffer); + } + + std::size_t startPosition = 0; + while (buffer.size() > 0 && !m_shuttingDown) { + if (context.remainingBytesToStrip == 0) { + auto it = std::search( + buffer.begin() + startPosition, + buffer.end(), + std::begin(ID3V2TAG_IDENTIFIER), + std::end(ID3V2TAG_IDENTIFIER)); + if (it != buffer.end()) { + if (!context.isBufferComplete && + std::distance(it, buffer.end()) <= static_cast(ID3V2TAG_HEADER_SIZE)) { + ACSDK_DEBUG9(LX("Partial ID3 tags").d("distanceFromEnd", std::distance(it, buffer.end()))); + context.remainingBuffer.insert(context.remainingBuffer.end(), it, buffer.end()); + buffer.erase(it, buffer.end()); + break; + } + + auto index = std::distance(buffer.begin(), it) + startPosition; + auto id3TagSize = getID3v2TagSize(buffer.data() + index, buffer.size() - index); + if (id3TagSize > 0) { + context.remainingBytesToStrip = id3TagSize; + startPosition = index; + } else { + // it doesn't match, shift one and search again + startPosition = index + sizeof(ID3V2TAG_IDENTIFIER); + // make sure the startPosition is still inbound, if not, break from loop. + if (startPosition >= buffer.size()) { + break; + } + continue; + } + } else { + if (!context.isBufferComplete) { + // check if last characters are "ID" or "I" and put them in remainingBuffer if it's the case. + auto remainingIdentifier = sizeof(ID3V2TAG_IDENTIFIER) - 1; + for (auto i = remainingIdentifier; i > 0; --i) { + if (buffer.size() >= i) { + auto lastIt = std::search( + buffer.end() - i, + buffer.end(), + std::begin(ID3V2TAG_IDENTIFIER), + std::begin(ID3V2TAG_IDENTIFIER) + i); + if (lastIt != buffer.end()) { + ACSDK_DEBUG9(LX("Partial ID3 tags").d("i", i)); + context.remainingBuffer.insert(context.remainingBuffer.end(), lastIt, buffer.end()); + buffer.erase(lastIt, buffer.end()); + break; + } + } + } + } + break; + } + } + + // Strip ID3 header if it exists + if (context.remainingBytesToStrip > 0) { + std::size_t strippedSize = 0; + if (context.remainingBytesToStrip + startPosition >= buffer.size()) { + context.remainingBytesToStrip -= (buffer.size() - startPosition); + strippedSize = buffer.size() - startPosition; + } else { + strippedSize = context.remainingBytesToStrip; + context.remainingBytesToStrip = 0; + } + ACSDK_DEBUG9(LX("ID3 header stripped") + .d("startPosition", startPosition) + .d("strippedSize", strippedSize) + .d("remainingBytesToStrip", context.remainingBytesToStrip) + .d("bytesRead", buffer.size())); + if (strippedSize == 0) { + break; + } + buffer.erase(buffer.begin() + startPosition, buffer.begin() + startPosition + strippedSize); + } + } +} + +bool Id3TagsRemover::writeBufferToWriter( + const ByteVector& buffer, + const std::shared_ptr& writer) { + if (buffer.size() == 0) { + return true; + } + + std::size_t targetNumBytes = buffer.size(); + std::size_t totalBytesWritten = 0; + const unsigned char* data = buffer.data(); + + while ((totalBytesWritten < targetNumBytes) && !m_shuttingDown) { + auto writeStatus = avsCommon::avs::attachment::AttachmentWriter::WriteStatus::OK; + + std::size_t numBytesWritten = + writer->write(data, targetNumBytes - totalBytesWritten, &writeStatus, WAIT_FOR_ACTIVITY_TIMEOUT); + totalBytesWritten += numBytesWritten; + data += numBytesWritten; + + switch (writeStatus) { + case avsCommon::avs::attachment::AttachmentWriter::WriteStatus::CLOSED: + case avsCommon::avs::attachment::AttachmentWriter::WriteStatus::ERROR_BYTES_LESS_THAN_WORD_SIZE: + case avsCommon::avs::attachment::AttachmentWriter::WriteStatus::ERROR_INTERNAL: + return false; + case avsCommon::avs::attachment::AttachmentWriter::WriteStatus::TIMEDOUT: + case avsCommon::avs::attachment::AttachmentWriter::WriteStatus::OK: + // might still have bytes to write + continue; + case avsCommon::avs::attachment::AttachmentWriter::WriteStatus::OK_BUFFER_FULL: + ACSDK_ERROR(LX(__func__).d("unexpected return code", writeStatus)); + return false; + } + ACSDK_ERROR(LX("UnexpectedWriteStatus").d("writeStatus", writeStatus)); + return false; + } + return true; +} + +void Id3TagsRemover::doShutdown() { + m_shuttingDown = true; +} + +} // namespace playlistParser +} // namespace alexaClientSDK diff --git a/PlaylistParser/src/UrlContentToAttachmentConverter.cpp b/PlaylistParser/src/UrlContentToAttachmentConverter.cpp index 9d61436e39..53fbf81974 100644 --- a/PlaylistParser/src/UrlContentToAttachmentConverter.cpp +++ b/PlaylistParser/src/UrlContentToAttachmentConverter.cpp @@ -89,6 +89,7 @@ UrlContentToAttachmentConverter::UrlContentToAttachmentConverter( m_stream = std::make_shared(url, nullptr, numOfReaders); m_streamWriter = m_stream->createWriter(avsCommon::utils::sds::WriterPolicy::BLOCKING); m_contentDecrypter = std::make_shared(); + m_id3TagsRemover = std::make_shared(); } std::chrono::milliseconds UrlContentToAttachmentConverter::getStartStreamingPoint() { @@ -240,15 +241,35 @@ bool UrlContentToAttachmentConverter::writeDecryptedUrlContentIntoStream( return false; } - if (!m_shuttingDown && !m_contentDecrypter->decryptAndWrite(content, key, encryptionInfo, m_streamWriter)) { + if (!m_shuttingDown && + !m_contentDecrypter->decryptAndWrite(content, key, encryptionInfo, m_streamWriter, m_id3TagsRemover)) { ACSDK_ERROR(LX("writeDecryptedUrlContentIntoStreamFailed").d("reason", "decryptAndWriteFailed")); return false; } } else { - if (!download(url, headers, m_streamWriter, contentFetcher)) { + bool returnValue = true; + auto attachment = std::make_shared("download:" + url); + + // start separate thread to download content to the new attachment + std::thread writerThread([this, url, headers, attachment, contentFetcher, &returnValue]() { + std::shared_ptr streamWriter = attachment->createWriter(WriterPolicy::BLOCKING); + if (!download(url, headers, streamWriter, contentFetcher)) { + ACSDK_ERROR(LX("downloadFailed").d("reason", "downloadToStreamFailed")); + returnValue = false; + } + streamWriter->close(); + }); + + // remove ID3 tags from new attachment and write to m_streamWriter + if (!m_id3TagsRemover->removeTagsAndWrite(attachment, m_streamWriter)) { ACSDK_ERROR(LX("writeDecryptedUrlContentIntoStreamFailed").d("reason", "downloadFailed")); - return false; + returnValue = false; + } + + if (writerThread.joinable()) { + writerThread.join(); } + return returnValue; } ACSDK_DEBUG9(LX("writeDecryptedUrlContentIntoStreamSuccess")); @@ -378,8 +399,10 @@ void UrlContentToAttachmentConverter::doShutdown() { } m_shuttingDown = true; m_contentDecrypter->shutdown(); + m_id3TagsRemover->shutdown(); m_executor.shutdown(); m_contentDecrypter.reset(); + m_id3TagsRemover.reset(); m_playlistParser->shutdown(); m_playlistParser.reset(); m_streamWriter->close(); diff --git a/PlaylistParser/test/ContentDecrypterTest.cpp b/PlaylistParser/test/ContentDecrypterTest.cpp index e5dbbbf602..5d74b72fa5 100644 --- a/PlaylistParser/test/ContentDecrypterTest.cpp +++ b/PlaylistParser/test/ContentDecrypterTest.cpp @@ -73,6 +73,9 @@ class ContentDecrypterTest : public ::testing::Test { /// Instance of the @c IterativePlaylistParser. std::shared_ptr m_decrypter; + + /// Instance of the @c ID3TagsRemover. + std::shared_ptr m_id3TagsRemover; }; void ContentDecrypterTest::SetUp() { @@ -81,6 +84,7 @@ void ContentDecrypterTest::SetUp() { m_reader = m_attachment->createReader(ReaderPolicy::NONBLOCKING); m_decrypter = std::make_shared(); + m_id3TagsRemover = std::make_shared(); } void ContentDecrypterTest::TearDown() { @@ -88,7 +92,11 @@ void ContentDecrypterTest::TearDown() { m_writer.reset(); m_reader.reset(); + m_decrypter->shutdown(); m_decrypter.reset(); + + m_id3TagsRemover->shutdown(); + m_id3TagsRemover.reset(); } std::string ContentDecrypterTest::readDecryptedContent(size_t readSize) { @@ -103,7 +111,7 @@ std::string ContentDecrypterTest::readDecryptedContent(size_t readSize) { TEST_F(ContentDecrypterTest, test_unsupportedEncryption) { auto noEncryption = EncryptionInfo(); - auto result = m_decrypter->decryptAndWrite(AES_ENCRYPTED_CONTENT, KEY, noEncryption, m_writer); + auto result = m_decrypter->decryptAndWrite(AES_ENCRYPTED_CONTENT, KEY, noEncryption, m_writer, m_id3TagsRemover); EXPECT_FALSE(result); } @@ -112,7 +120,8 @@ TEST_F(ContentDecrypterTest, test_invalidKeyEncryption) { /// Test key: aaaaaaaaaaaaaaa. Length is invalid. static const ByteVector INVALID_KEY(15, 0x61); - auto result = m_decrypter->decryptAndWrite(AES_ENCRYPTED_CONTENT, INVALID_KEY, AES_ENCRYPTION_INFO, m_writer); + auto result = m_decrypter->decryptAndWrite( + AES_ENCRYPTED_CONTENT, INVALID_KEY, AES_ENCRYPTION_INFO, m_writer, m_id3TagsRemover); EXPECT_FALSE(result); } @@ -125,13 +134,15 @@ TEST_F(ContentDecrypterTest, test_invalidIVEncryption) { static const auto INVALID_AES_ENCRYPTION_INFO = EncryptionInfo(EncryptionInfo::Method::AES_128, "https://wwww.amazon.com/key.txt", INVALID_HEX_IV); - auto result = m_decrypter->decryptAndWrite(AES_ENCRYPTED_CONTENT, KEY, INVALID_AES_ENCRYPTION_INFO, m_writer); + auto result = m_decrypter->decryptAndWrite( + AES_ENCRYPTED_CONTENT, KEY, INVALID_AES_ENCRYPTION_INFO, m_writer, m_id3TagsRemover); EXPECT_FALSE(result); } TEST_F(ContentDecrypterTest, test_aESDecryption) { - auto result = m_decrypter->decryptAndWrite(AES_ENCRYPTED_CONTENT, KEY, AES_ENCRYPTION_INFO, m_writer); + auto result = + m_decrypter->decryptAndWrite(AES_ENCRYPTED_CONTENT, KEY, AES_ENCRYPTION_INFO, m_writer, m_id3TagsRemover); auto decryptedString = readDecryptedContent(DECRYPTED_STRING.size()); EXPECT_TRUE(result); diff --git a/PlaylistParser/test/Id3TagsRemoverTest.cpp b/PlaylistParser/test/Id3TagsRemoverTest.cpp new file mode 100644 index 0000000000..6779906560 --- /dev/null +++ b/PlaylistParser/test/Id3TagsRemoverTest.cpp @@ -0,0 +1,348 @@ +/* + * Copyright 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 +#include + +#include + +#include "PlaylistParser/Id3TagsRemover.h" + +namespace alexaClientSDK { +namespace playlistParser { +namespace test { + +/// Alias for bytes. +using ByteVector = std::vector; + +/// A timeout for the attachment reader +static const std::chrono::milliseconds WAIT_FOR_READ_TIMEOUT{1000}; + +/// An valid ID3 tag with tag size 11 (header + 1). +static const ByteVector VALID_ID3_TAG{'I', 'D', '3', 4, 0, 0, 0, 0, 0, 1}; + +using namespace avsCommon::avs::attachment; +using namespace avsCommon::utils::sds; +using namespace ::testing; + +/// Test class for Id3TagsRemoverTest class. +class Id3TagsRemoverTest : public ::testing::Test { +protected: + /// Configure test instance. + void SetUp() override; + + /// Tear down test instance. + void TearDown() override; + + /** + * Helper method to write content1 and content2 (if exist) into @c m_contentAttachment, and read data from + * @c m_removerAttachment and check if the result are expected. + * + * @param content1 The content to write to m_contentAttachment. + * @param expectedResult The expected result after ID3 tags are removed. + * @param content2 The content (if exist) to write to m_contentAttachment. + */ + void readContentAftersRemoval( + const ByteVector& content1, + const ByteVector& expectedResult, + const ByteVector& content2 = ByteVector{}); + + /// Attachment used origin content. + std::shared_ptr m_contentAttachment; + + /// Writer to write content with tags removed. + std::shared_ptr m_contentWriter; + + /// Attachment used by ID3 tag remover. + std::shared_ptr m_removerAttachment; + + /// Writer to write content with tags removed. + std::shared_ptr m_removerWriter; + + /// Reader to read content with tags removed. + std::shared_ptr m_removerReader; + + /// Instance of the @c Id3TagsRemover. + std::shared_ptr m_id3TagsRemover; +}; + +void Id3TagsRemoverTest::SetUp() { + m_contentAttachment = std::make_shared("content"); + m_contentWriter = m_contentAttachment->createWriter(WriterPolicy::BLOCKING); + + m_removerAttachment = std::make_shared("remover"); + m_removerWriter = m_removerAttachment->createWriter(WriterPolicy::BLOCKING); + m_removerReader = m_removerAttachment->createReader(ReaderPolicy::BLOCKING); + + m_id3TagsRemover = std::make_shared(); +} + +void Id3TagsRemoverTest::TearDown() { + m_contentAttachment.reset(); + m_contentWriter.reset(); + + m_removerAttachment.reset(); + m_removerWriter.reset(); + m_removerReader.reset(); + + m_id3TagsRemover->shutdown(); + m_id3TagsRemover.reset(); +} + +void Id3TagsRemoverTest::readContentAftersRemoval( + const ByteVector& content1, + const ByteVector& expectedResult, + const ByteVector& content2) { + std::thread writerThread([this, &content1, &content2, &expectedResult]() { + m_id3TagsRemover->removeTagsAndWrite(m_contentAttachment, m_removerWriter); + + ByteVector buffer(content1.size() + content2.size(), 0); + auto readStatus = AttachmentReader::ReadStatus::OK; + auto numRead = m_removerReader->read(buffer.data(), buffer.size(), &readStatus, WAIT_FOR_READ_TIMEOUT); + buffer.resize(numRead); + + EXPECT_EQ(buffer.size(), expectedResult.size()); + EXPECT_EQ(buffer, expectedResult); + }); + + auto writeStatus = avsCommon::avs::attachment::AttachmentWriter::WriteStatus::OK; + m_contentWriter->write(content1.data(), content1.size(), &writeStatus); + if (content2.size() > 0) { + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + m_contentWriter->write(content2.data(), content2.size(), &writeStatus); + } + m_contentWriter->close(); + if (writerThread.joinable()) { + writerThread.join(); + } +} + +TEST_F(Id3TagsRemoverTest, test_validID3Tag) { + ByteVector buffer{VALID_ID3_TAG}; + + // valid ID3 tag says ID3 tag contains 1 byte of data, so after remover, it should contain 3 bytes + ByteVector testData{'a', 'b', 'c', 'd'}; + buffer.insert(buffer.end(), testData.begin(), testData.end()); + + m_id3TagsRemover->stripID3Tags(buffer); + + // Expect 'b', 'c', 'd' + testData.erase(testData.begin()); + EXPECT_EQ(buffer.size(), testData.size()); + EXPECT_EQ(buffer, testData); +} + +TEST_F(Id3TagsRemoverTest, test_validID3TagWithOffset) { + ByteVector buffer{'a'}; + buffer.insert(buffer.end(), VALID_ID3_TAG.begin(), VALID_ID3_TAG.end()); + + // valid ID3 tag says ID3 tag contains 1 byte of data, so after remover, it should contain 3 bytes + ByteVector testData{'a', 'b', 'c', 'd'}; + buffer.insert(buffer.end(), testData.begin(), testData.end()); + + m_id3TagsRemover->stripID3Tags(buffer); + + // Expect 'a', 'b', 'c', 'd' + EXPECT_EQ(buffer.size(), testData.size()); + EXPECT_EQ(buffer, testData); +} + +TEST_F(Id3TagsRemoverTest, test_twoValidID3Tag) { + ByteVector buffer{VALID_ID3_TAG}; + + // valid ID3 tag says ID3 tag contains 1 byte of data, so after remover, it should contain 3 bytes + ByteVector testData{'a', 'b', 'c', 'd'}; + buffer.insert(buffer.end(), testData.begin(), testData.end()); + buffer.insert(buffer.end(), VALID_ID3_TAG.begin(), VALID_ID3_TAG.end()); + buffer.insert(buffer.end(), testData.begin(), testData.end()); + + m_id3TagsRemover->stripID3Tags(buffer); + + // Expect 'b', 'c', 'd', 'b', 'c', 'd' + testData.erase(testData.begin()); + testData.insert(testData.end(), testData.begin(), testData.end()); + EXPECT_EQ(buffer.size(), testData.size()); + EXPECT_EQ(buffer, testData); +} + +TEST_F(Id3TagsRemoverTest, test_invalidID3Tag) { + ByteVector buffer{'I', 'D', '3', 99, 88, 77, 66, 55, 44, 33, 22}; + ByteVector expectedResult{buffer}; + + m_id3TagsRemover->stripID3Tags(buffer); + + // Expect 'I', 'D', '3', 99, 88, 77 + EXPECT_EQ(buffer.size(), expectedResult.size()); + EXPECT_EQ(buffer, expectedResult); +} + +TEST_F(Id3TagsRemoverTest, test_partialID3Tag) { + ByteVector bufferID3{'I', 'D', '3'}; + ByteVector expectedResultID3{bufferID3}; + + m_id3TagsRemover->stripID3Tags(bufferID3); + + // Expect 'I', 'D', '3' + EXPECT_EQ(bufferID3.size(), expectedResultID3.size()); + EXPECT_EQ(bufferID3, expectedResultID3); + + ByteVector bufferID{'I', 'D'}; + ByteVector expectedResultID{bufferID}; + + m_id3TagsRemover->stripID3Tags(bufferID); + + // Expect 'I', 'D' + EXPECT_EQ(bufferID.size(), expectedResultID.size()); + EXPECT_EQ(bufferID, expectedResultID); + + ByteVector bufferI{'I'}; + ByteVector expectedResultI{bufferI}; + + m_id3TagsRemover->stripID3Tags(bufferI); + + // Expect 'I', 'D' + EXPECT_EQ(bufferI.size(), expectedResultI.size()); + EXPECT_EQ(bufferI, expectedResultI); +} + +TEST_F(Id3TagsRemoverTest, test_attachmentValidID3Tag) { + ByteVector buffer{VALID_ID3_TAG}; + + // valid ID3 tag says ID3 tag contains 1 byte of data, so after remover, it should contain 3 bytes + ByteVector testData{'a', 'b', 'c', 'd'}; + buffer.insert(buffer.end(), testData.begin(), testData.end()); + + ByteVector expectedResult{'b', 'c', 'd'}; + readContentAftersRemoval(buffer, expectedResult); +} + +TEST_F(Id3TagsRemoverTest, test_attachmentTwoValidID3Tag) { + ByteVector buffer{VALID_ID3_TAG}; + + // valid ID3 tag says ID3 tag contains 1 byte of data, so after remover, it should contain 3 bytes + ByteVector testData{'a', 'b', 'c', 'd'}; + buffer.insert(buffer.end(), testData.begin(), testData.end()); + buffer.insert(buffer.end(), VALID_ID3_TAG.begin(), VALID_ID3_TAG.end()); + buffer.insert(buffer.end(), testData.begin(), testData.end()); + + // Expect 'b', 'c', 'd', 'b', 'c', 'd' + testData.erase(testData.begin()); + testData.insert(testData.end(), testData.begin(), testData.end()); + + readContentAftersRemoval(buffer, testData); +} + +TEST_F(Id3TagsRemoverTest, test_attachmentPartialID3Tag) { + ByteVector bufferID3{'I', 'D', '3'}; + ByteVector expectedResultID3{bufferID3}; + + readContentAftersRemoval(bufferID3, expectedResultID3); +} + +TEST_F(Id3TagsRemoverTest, test_attachmentCompleteID3Tag) { + ByteVector bufferID3{VALID_ID3_TAG}; + ByteVector expectedResultID3{}; + + readContentAftersRemoval(bufferID3, expectedResultID3); +} + +TEST_F(Id3TagsRemoverTest, test_attachmentID3TagAcrossTwoWrites) { + ByteVector content1{'a', 'b', 'c', 'I'}; + ByteVector content2{VALID_ID3_TAG}; + content2.erase(content2.begin()); + content2.insert(content2.end(), content1.begin(), content1.end()); + + ByteVector expectedResult{'a', 'b', 'c', 'b', 'c', 'I'}; + + readContentAftersRemoval(content1, expectedResult, content2); +} + +TEST_F(Id3TagsRemoverTest, test_attachmentID3Tag9MatchingAcrossTwoWrites) { + ByteVector content1{VALID_ID3_TAG}; + content1.erase(content1.end() - 1); + ByteVector content2{1, 'b', 'c', 'I'}; + + ByteVector expectedResult{'c', 'I'}; + + readContentAftersRemoval(content1, expectedResult, content2); +} + +TEST_F(Id3TagsRemoverTest, test_attachmentID3TagAcrossFullHeaderTwoWrites) { + ByteVector content1{VALID_ID3_TAG}; + ByteVector content2{'a', 'b', 'c', 'I'}; + + ByteVector expectedResult{'b', 'c', 'I'}; + + readContentAftersRemoval(content1, expectedResult, content2); +} + +TEST_F(Id3TagsRemoverTest, test_attachmentInvalidID3TagAcrossTwoWrites) { + ByteVector content1{'a', 'b', 'c', 'I'}; + ByteVector content2{'D', 'a', 'b', 'c'}; + + ByteVector expectedResult{'a', 'b', 'c', 'I', 'D', 'a', 'b', 'c'}; + + readContentAftersRemoval(content1, expectedResult, content2); +} + +TEST_F(Id3TagsRemoverTest, test_attachmentID3TagRemove10BytesAcrossTwoWrites) { + ByteVector content1{{'I', 'D', '3', 4, 0, 0, 0, 0, 0, 10, '1', '2', '3', '4'}}; + ByteVector content2{'5', '6', '7', '8', '9', 'a', 'b', 'c'}; + + ByteVector expectedResult{'b', 'c'}; + + readContentAftersRemoval(content1, expectedResult, content2); +} + +TEST_F(Id3TagsRemoverTest, test_attachmentID3TagBoundary1AcrossTwoWrites) { + ByteVector content1{'I'}; + ByteVector content2{'D', '3', 4, 0, 0, 0, 0, 0, 1, '1', '2'}; + + ByteVector expectedResult{'2'}; + + readContentAftersRemoval(content1, expectedResult, content2); +} + +TEST_F(Id3TagsRemoverTest, test_attachmentID3TagBoundary2AcrossTwoWrites) { + ByteVector content1{'I', 'D'}; + ByteVector content2{'3', 3, 0, 0, 0, 0, 0, 1, '1', '2'}; + + ByteVector expectedResult{'2'}; + + readContentAftersRemoval(content1, expectedResult, content2); +} + +TEST_F(Id3TagsRemoverTest, test_attachmentID3TagBoundary3AcrossTwoWrites) { + ByteVector content1{'I', 'D', '3'}; + ByteVector content2{'1', '2'}; + + ByteVector expectedResult{'I', 'D', '3', '1', '2'}; + + readContentAftersRemoval(content1, expectedResult, content2); +} + +TEST_F(Id3TagsRemoverTest, test_attachmentID3TagRemoveTagAcrossTwoBoundaries) { + ByteVector content1{'1', '2', 'I', 'D', '3', 4, 0, 0, 0, 0, 0, 5, '1', '2'}; + + ByteVector content2{'3', '4', '5', '6', '7'}; + + ByteVector expectedResult{'1', '2', '6', '7'}; + + readContentAftersRemoval(content1, expectedResult, content2); +} + +} // namespace test +} // namespace playlistParser +} // namespace alexaClientSDK diff --git a/PlaylistParser/test/PlaylistParser/MockContentFetcher.h b/PlaylistParser/test/PlaylistParser/MockContentFetcher.h index 07244f4cc0..6cb6c541a1 100644 --- a/PlaylistParser/test/PlaylistParser/MockContentFetcher.h +++ b/PlaylistParser/test/PlaylistParser/MockContentFetcher.h @@ -223,7 +223,9 @@ static std::unordered_map urlsToContent{ /// A mock content fetcher class MockContentFetcher : public HTTPContentFetcherInterface { public: - MockContentFetcher(const std::string& url) : m_url{url}, m_state{HTTPContentFetcherInterface::State::INITIALIZED} { + explicit MockContentFetcher(const std::string& url) : + m_url{url}, + m_state{HTTPContentFetcherInterface::State::INITIALIZED} { } std::string getUrl() const override { diff --git a/PlaylistParser/test/PlaylistParserTest.cpp b/PlaylistParser/test/PlaylistParserTest.cpp index dd10bd4ab7..027a187ac9 100644 --- a/PlaylistParser/test/PlaylistParserTest.cpp +++ b/PlaylistParser/test/PlaylistParserTest.cpp @@ -39,7 +39,7 @@ using namespace avsCommon::avs; /// A mock factory that creates mock content fetchers class MockContentFetcherFactory : public avsCommon::sdkInterfaces::HTTPContentFetcherInterfaceFactoryInterface { - std::unique_ptr create(const std::string& url) { + std::unique_ptr create(const std::string& url) override { return avsCommon::utils::memory::make_unique(url); } }; @@ -92,13 +92,13 @@ class TestParserObserver : public avsCommon::utils::playlistParser::PlaylistPars class PlaylistParserTest : public ::testing::Test { protected: - void SetUp() { + void SetUp() override { mockFactory = std::make_shared(); playlistParser = PlaylistParser::create(mockFactory); testObserver = std::make_shared(); } - void TearDown() { + void TearDown() override { playlistParser->shutdown(); } diff --git a/RegistrationManager/test/CustomerDataManagerTest.cpp b/RegistrationManager/test/CustomerDataManagerTest.cpp index 9f35f4e62e..4eca4ab1fb 100644 --- a/RegistrationManager/test/CustomerDataManagerTest.cpp +++ b/RegistrationManager/test/CustomerDataManagerTest.cpp @@ -27,7 +27,8 @@ namespace test { class MockCustomerDataHandler : public CustomerDataHandler { public: - MockCustomerDataHandler(std::shared_ptr dataManager) : CustomerDataHandler(dataManager) { + explicit MockCustomerDataHandler(std::shared_ptr dataManager) : + CustomerDataHandler(dataManager) { } MOCK_METHOD0(clearData, void()); }; diff --git a/RegistrationManager/test/RegistrationManagerTest.cpp b/RegistrationManager/test/RegistrationManagerTest.cpp index 5a8818cf15..d395894276 100644 --- a/RegistrationManager/test/RegistrationManagerTest.cpp +++ b/RegistrationManager/test/RegistrationManagerTest.cpp @@ -42,7 +42,7 @@ class MockRegistrationObserver : public RegistrationObserverInterface { class MockCustomerDataHandler : public CustomerDataHandler { public: - MockCustomerDataHandler(std::shared_ptr manager) : CustomerDataHandler{manager} { + explicit MockCustomerDataHandler(std::shared_ptr manager) : CustomerDataHandler{manager} { } MOCK_METHOD0(clearData, void()); }; diff --git a/SampleApp/src/ConsoleReader.cpp b/SampleApp/src/ConsoleReader.cpp index c68672fe9f..74aa2e7618 100644 --- a/SampleApp/src/ConsoleReader.cpp +++ b/SampleApp/src/ConsoleReader.cpp @@ -57,7 +57,6 @@ bool ConsoleReader::read(const std::chrono::milliseconds timeout, char* data) { void ConsoleReader::workerLoop() { while (!m_shutDown) { - char tempChar; std::unique_lock lock(m_mutex); if (m_waitOnEvent.wait_for(lock, READ_REQUEST_PENDING_TIMEOUT, [this] { return m_shutDown || State::PENDING_REQUEST == m_state; @@ -65,6 +64,7 @@ void ConsoleReader::workerLoop() { if (m_shutDown) { break; } + char tempChar; m_state = State::READING; lock.unlock(); diff --git a/SampleApp/src/DefaultEndpoint/DefaultEndpointModeControllerHandler.cpp b/SampleApp/src/DefaultEndpoint/DefaultEndpointModeControllerHandler.cpp index e9df516ee6..b9014bfafd 100644 --- a/SampleApp/src/DefaultEndpoint/DefaultEndpointModeControllerHandler.cpp +++ b/SampleApp/src/DefaultEndpoint/DefaultEndpointModeControllerHandler.cpp @@ -122,7 +122,6 @@ std::pair DefaultEndpointModeControllerHandler:: int modeDelta, AlexaStateChangeCauseType cause) { std::list> copyOfObservers; - bool notifyObserver = false; std::unique_lock lock(m_mutex); auto itr = std::find(m_modes.begin(), m_modes.end(), m_currentMode); @@ -140,17 +139,13 @@ std::pair DefaultEndpointModeControllerHandler:: ConsolePrinter::prettyPrint( {"ENDPOINT: Default Endpoint", "INSTANCE: " + m_instance, "ADJUSTED MODE TO: " + m_currentMode}); copyOfObservers = m_observers; - notifyObserver = true; lock.unlock(); - - if (notifyObserver) { - notifyObservers( - ModeControllerInterface::ModeState{ - m_currentMode, avsCommon::utils::timing::TimePoint::now(), std::chrono::milliseconds(0)}, - cause, - copyOfObservers); - } + notifyObservers( + ModeControllerInterface::ModeState{ + m_currentMode, avsCommon::utils::timing::TimePoint::now(), std::chrono::milliseconds(0)}, + cause, + copyOfObservers); return std::make_pair(AlexaResponseType::SUCCESS, ""); } diff --git a/SampleApp/src/DefaultEndpoint/DefaultEndpointRangeControllerHandler.cpp b/SampleApp/src/DefaultEndpoint/DefaultEndpointRangeControllerHandler.cpp index 6458f4c86d..da8439b2e7 100644 --- a/SampleApp/src/DefaultEndpoint/DefaultEndpointRangeControllerHandler.cpp +++ b/SampleApp/src/DefaultEndpoint/DefaultEndpointRangeControllerHandler.cpp @@ -97,8 +97,9 @@ std::pair DefaultEndpointRangeControllerHandler: } if (m_currentRangeValue != value) { + auto valueStr = std::to_string(value); ConsolePrinter::prettyPrint( - {"ENDPOINT: Default Endpoint", "INSTANCE: " + m_instance, "RANGE SET TO: " + std::to_string(value)}); + {"ENDPOINT: Default Endpoint", "INSTANCE: " + m_instance, "RANGE SET TO: " + valueStr}); m_currentRangeValue = value; copyOfObservers = m_observers; notifyObserver = true; @@ -121,7 +122,6 @@ std::pair DefaultEndpointRangeControllerHandler: double deltaValue, AlexaStateChangeCauseType cause) { std::list> copyOfObservers; - bool notifyObserver = false; std::unique_lock lock(m_mutex); auto newvalue = m_currentRangeValue + deltaValue; @@ -131,21 +131,18 @@ std::pair DefaultEndpointRangeControllerHandler: } m_currentRangeValue += deltaValue; - ConsolePrinter::prettyPrint({"ENDPOINT: Default Endpoint", - "INSTANCE: " + m_instance, - "ADJUSTED RANGE TO : " + std::to_string(m_currentRangeValue)}); + auto rangeValueStr = std::to_string(m_currentRangeValue); + ConsolePrinter::prettyPrint( + {"ENDPOINT: Default Endpoint", "INSTANCE: " + m_instance, "ADJUSTED RANGE TO : " + rangeValueStr}); copyOfObservers = m_observers; - notifyObserver = true; lock.unlock(); - if (notifyObserver) { - notifyObservers( - RangeControllerInterface::RangeState{ - m_currentRangeValue, avsCommon::utils::timing::TimePoint::now(), std::chrono::milliseconds(0)}, - cause, - copyOfObservers); - } + notifyObservers( + RangeControllerInterface::RangeState{ + m_currentRangeValue, avsCommon::utils::timing::TimePoint::now(), std::chrono::milliseconds(0)}, + cause, + copyOfObservers); return std::make_pair(AlexaResponseType::SUCCESS, ""); } diff --git a/SampleApp/src/ExternalCapabilitiesBuilder.cpp b/SampleApp/src/ExternalCapabilitiesBuilder.cpp index 4167533483..cb3db7ccd0 100644 --- a/SampleApp/src/ExternalCapabilitiesBuilder.cpp +++ b/SampleApp/src/ExternalCapabilitiesBuilder.cpp @@ -234,7 +234,7 @@ ExternalCapabilitiesBuilder::buildCapabilities( Capability mrmCapability; auto mrmConfigurations = mrmCapabilityAgent->getCapabilityConfigurations(); - mrmCapability.directiveHandler = std::move(mrmCapabilityAgent); + mrmCapability.directiveHandler = mrmCapabilityAgent; for (auto& configurationPtr : mrmConfigurations) { mrmCapability.configuration = *configurationPtr; capabilities.push_back(mrmCapability); diff --git a/SampleApp/src/PeripheralEndpoint/PeripheralEndpointModeControllerHandler.cpp b/SampleApp/src/PeripheralEndpoint/PeripheralEndpointModeControllerHandler.cpp index a5cc9775b7..f35333fda2 100644 --- a/SampleApp/src/PeripheralEndpoint/PeripheralEndpointModeControllerHandler.cpp +++ b/SampleApp/src/PeripheralEndpoint/PeripheralEndpointModeControllerHandler.cpp @@ -120,7 +120,6 @@ std::pair PeripheralEndpointModeControllerHandle int modeDelta, AlexaStateChangeCauseType cause) { std::list> copyOfObservers; - bool notifyObserver = false; std::unique_lock lock(m_mutex); auto itr = std::find(m_modes.begin(), m_modes.end(), m_currentMode); @@ -138,17 +137,13 @@ std::pair PeripheralEndpointModeControllerHandle ConsolePrinter::prettyPrint( {"ENDPOINT: " + m_endpointName, "INSTANCE: " + m_instance, "ADJUSTED MODE TO: " + m_currentMode}); copyOfObservers = m_observers; - notifyObserver = true; lock.unlock(); - - if (notifyObserver) { - notifyObservers( - ModeControllerInterface::ModeState{ - m_currentMode, avsCommon::utils::timing::TimePoint::now(), std::chrono::milliseconds(0)}, - cause, - copyOfObservers); - } + notifyObservers( + ModeControllerInterface::ModeState{ + m_currentMode, avsCommon::utils::timing::TimePoint::now(), std::chrono::milliseconds(0)}, + cause, + copyOfObservers); return std::make_pair(AlexaResponseType::SUCCESS, ""); } diff --git a/SampleApp/src/PeripheralEndpoint/PeripheralEndpointPowerControllerHandler.cpp b/SampleApp/src/PeripheralEndpoint/PeripheralEndpointPowerControllerHandler.cpp index 23a186efdf..2ca4f3a599 100644 --- a/SampleApp/src/PeripheralEndpoint/PeripheralEndpointPowerControllerHandler.cpp +++ b/SampleApp/src/PeripheralEndpoint/PeripheralEndpointPowerControllerHandler.cpp @@ -71,7 +71,6 @@ std::pair PeripheralEndpointPowerControllerHandl bool state, AlexaStateChangeCauseType cause) { std::list> copyOfObservers; - bool notifyObserver = false; std::unique_lock lock(m_mutex); if (m_currentPowerState != state) { @@ -80,22 +79,18 @@ std::pair PeripheralEndpointPowerControllerHandl m_currentPowerState = state; copyOfObservers = m_observers; - notifyObserver = true; } m_currentPowerState = state; copyOfObservers = m_observers; - notifyObserver = true; lock.unlock(); - if (notifyObserver) { - notifyObservers( - PowerControllerInterface::PowerState{ - m_currentPowerState, avsCommon::utils::timing::TimePoint::now(), std::chrono::milliseconds(0)}, - cause, - copyOfObservers); - } + notifyObservers( + PowerControllerInterface::PowerState{ + m_currentPowerState, avsCommon::utils::timing::TimePoint::now(), std::chrono::milliseconds(0)}, + cause, + copyOfObservers); return std::make_pair(AlexaResponseType::SUCCESS, ""); } diff --git a/SampleApp/src/PeripheralEndpoint/PeripheralEndpointRangeControllerHandler.cpp b/SampleApp/src/PeripheralEndpoint/PeripheralEndpointRangeControllerHandler.cpp index 346094d84e..91d7e29641 100644 --- a/SampleApp/src/PeripheralEndpoint/PeripheralEndpointRangeControllerHandler.cpp +++ b/SampleApp/src/PeripheralEndpoint/PeripheralEndpointRangeControllerHandler.cpp @@ -125,7 +125,6 @@ std::pair PeripheralEndpointRangeControllerHandl double deltaValue, AlexaStateChangeCauseType cause) { std::list> copyOfObservers; - bool notifyObserver = false; std::unique_lock lock(m_mutex); auto newvalue = m_currentRangeValue + deltaValue; @@ -139,17 +138,14 @@ std::pair PeripheralEndpointRangeControllerHandl "INSTANCE: " + m_instance, "ADJUSTED RANGE TO : " + std::to_string(m_currentRangeValue)}); copyOfObservers = m_observers; - notifyObserver = true; lock.unlock(); - if (notifyObserver) { - notifyObservers( - RangeControllerInterface::RangeState{ - m_currentRangeValue, avsCommon::utils::timing::TimePoint::now(), std::chrono::milliseconds(0)}, - cause, - copyOfObservers); - } + notifyObservers( + RangeControllerInterface::RangeState{ + m_currentRangeValue, avsCommon::utils::timing::TimePoint::now(), std::chrono::milliseconds(0)}, + cause, + copyOfObservers); return std::make_pair(AlexaResponseType::SUCCESS, ""); } diff --git a/SampleApp/src/SampleApplication.cpp b/SampleApp/src/SampleApplication.cpp index 9b521482df..ad9756c3a6 100644 --- a/SampleApp/src/SampleApplication.cpp +++ b/SampleApp/src/SampleApplication.cpp @@ -376,6 +376,7 @@ struct CapabilityResources { std::vector friendlyNames; }; +#ifdef RANGE_CONTROLLER /// This struct represents the Range Controller preset and its friendly names. struct RangeControllerPresetResources { /// The value of a preset. @@ -384,7 +385,9 @@ struct RangeControllerPresetResources { /// The friendly names of the presets. std::vector friendlyNames; }; +#endif +#ifdef MODE_CONTROLLER /// This struct represents the Mode Controller modes and its friendly names. struct ModeControllerModeResources { /// The mode in the Mode Controller. @@ -393,7 +396,7 @@ struct ModeControllerModeResources { /// The friendly names of the @c mode. std::vector friendlyNames; }; - +#endif #endif /** @@ -794,10 +797,10 @@ bool SampleApplication::initialize( configJsonStreams->push_back(configInFile); } - bool enableDucking = true; - #ifdef DISABLE_DUCKING - enableDucking = false; + bool enableDucking = false; +#else + bool enableDucking = true; #endif // Add the InterruptModel Configuration. diff --git a/Settings/include/Settings/DeviceSettingsManager.h b/Settings/include/Settings/DeviceSettingsManager.h index 65a7a1b919..5a815b81eb 100644 --- a/Settings/include/Settings/DeviceSettingsManager.h +++ b/Settings/include/Settings/DeviceSettingsManager.h @@ -23,9 +23,9 @@ #include "Settings/SettingInterface.h" #include "Settings/SettingsManager.h" #include "Settings/SpeechConfirmationSettingType.h" +#include "Settings/WakeWordConfirmationSettingType.h" #include "Settings/Types/AlarmVolumeRampTypes.h" #include "Settings/Types/NetworkInfo.h" -#include "Settings/WakeWordConfirmationSettingType.h" namespace alexaClientSDK { namespace settings { @@ -93,6 +93,17 @@ using DeviceSettingsManager = SettingsManager< LocalesSetting, NetworkInfoSetting>; +/// An alias to shorten the name. +using DeviceSettingManagerSettingConfigurations = std::tuple< + SettingConfiguration, + SettingConfiguration, + SettingConfiguration, + SettingConfiguration, + SettingConfiguration, + SettingConfiguration, + SettingConfiguration, + SettingConfiguration>; + } // namespace settings } // namespace alexaClientSDK diff --git a/Settings/include/Settings/SettingsManager.h b/Settings/include/Settings/SettingsManager.h index 05372d5f59..55ec78e1bd 100644 --- a/Settings/include/Settings/SettingsManager.h +++ b/Settings/include/Settings/SettingsManager.h @@ -22,16 +22,31 @@ #include #include +#include #include #include #include +#include "Settings/SettingEventMetadata.h" #include "Settings/SettingInterface.h" #include "Settings/SettingStringConversion.h" namespace alexaClientSDK { namespace settings { +/** + * Structure to save a specific setting and its configuration. + * + * @tparam SettingsT The type of the setting. + */ +template +struct SettingConfiguration { + /// The setting configured. + std::shared_ptr setting; + /// The setting metadata. + avsCommon::utils::Optional metadata; +}; + /** * The @c SettingsManager is responsible for managing settings. */ @@ -54,6 +69,9 @@ class SettingsManager : public registrationManager::CustomerDataHandler { template using SettingPointerType = std::shared_ptr; + /// The tuple holding the settings configuration. + using SettingConfigurations = std::tuple...>; + /// The number of settings supported by this manager. static constexpr size_t NUMBER_OF_SETTINGS{sizeof...(SettingsT)}; @@ -61,6 +79,17 @@ class SettingsManager : public registrationManager::CustomerDataHandler { * Settings manager constructor. * * @param dataManager A dataManager object that will track the CustomerDataHandler. + * @param settingConfigurations The tuple holding the settings configuration. + */ + SettingsManager( + std::shared_ptr dataManager, + SettingConfigurations settingConfigurations); + + /** + * Settings manager constructor. + * + * @deprecated + * @param dataManager A dataManager object that will track the CustomerDataHandler. */ SettingsManager(std::shared_ptr dataManager); @@ -147,6 +176,23 @@ class SettingsManager : public registrationManager::CustomerDataHandler { template bool hasSetting(); + /** + * Gets the settings configuration. + * + * @return the settings configuration. + */ + SettingConfigurations getConfigurations() const; + + /** + * Gets the setting for the given @c index. + * + * @tparam index The setting index. + * @return A pointer for the setting kept in @c index if the setting has been built; @c nullptr otherwise. + * @note This function should be used after @c build() has been called. + */ + template + std::shared_ptr> getSetting() const; + /// @name CustomerDataHandler Functions /// @{ void clearData() override; @@ -204,8 +250,19 @@ class SettingsManager : public registrationManager::CustomerDataHandler { // A tuple with all the settings supported by this manager. std::tuple...> m_settings; + + /// A tuple with all setting configurations. + SettingConfigurations m_settingConfigs; }; +template +SettingsManager::SettingsManager( + std::shared_ptr dataManager, + SettingConfigurations settingConfiguration) : + CustomerDataHandler{dataManager}, + m_settingConfigs{settingConfiguration} { +} + template SettingsManager::SettingsManager(std::shared_ptr dataManager) : CustomerDataHandler{dataManager} { @@ -330,6 +387,18 @@ void SettingsManager::clearData() { doClearData<0>(); } +template +std::tuple...> SettingsManager::getConfigurations() const { + return m_settingConfigs; +} + +template +template +std::shared_ptr::template SettingType> SettingsManager< + SettingsT...>::getSetting() const { + return std::get(m_settingConfigs).setting; +} + } // namespace settings } // namespace alexaClientSDK diff --git a/Settings/include/Settings/SettingsManagerBuilderBase.h b/Settings/include/Settings/SettingsManagerBuilderBase.h index 77f1dc0a2f..7e7177b869 100644 --- a/Settings/include/Settings/SettingsManagerBuilderBase.h +++ b/Settings/include/Settings/SettingsManagerBuilderBase.h @@ -25,19 +25,6 @@ namespace alexaClientSDK { namespace settings { -/** - * Structure to save a specific setting and its configuration. - * - * @tparam SettingsT The type of the setting. - */ -template -struct SettingConfiguration { - /// The setting configured. - std::shared_ptr setting; - /// The setting metadata. - avsCommon::utils::Optional metadata; -}; - /** * Base template for settings manager builders. * diff --git a/Settings/include/Settings/Storage/SQLiteDeviceSettingStorage.h b/Settings/include/Settings/Storage/SQLiteDeviceSettingStorage.h index 7ceba3e638..ce08ed18ac 100644 --- a/Settings/include/Settings/Storage/SQLiteDeviceSettingStorage.h +++ b/Settings/include/Settings/Storage/SQLiteDeviceSettingStorage.h @@ -43,6 +43,17 @@ class SQLiteDeviceSettingStorage : public DeviceSettingStorageInterface { * @param configurationRoot The global config object where the location of the misc database can be found. * @return Pointer to the SQLiteDeviceSettingStorage object, nullptr if there's an error creating it. */ + static std::shared_ptr createDeviceSettingStorageInterface( + const std::shared_ptr& configurationRoot); + + /** + * Factory method for creating a storage object for settings based on an SQLite database. + * Settings will be stored in the misc database. + * + * @deprecated Use createDeviceSettingStorageInterface + * @param configurationRoot The global config object where the location of the misc database can be found. + * @return Pointer to the SQLiteDeviceSettingStorage object, nullptr if there's an error creating it. + */ static std::unique_ptr create( const avsCommon::utils::configuration::ConfigurationNode& configurationRoot); diff --git a/Settings/src/Storage/SQLiteDeviceSettingStorage.cpp b/Settings/src/Storage/SQLiteDeviceSettingStorage.cpp index 6f2a92f550..561d53290d 100644 --- a/Settings/src/Storage/SQLiteDeviceSettingStorage.cpp +++ b/Settings/src/Storage/SQLiteDeviceSettingStorage.cpp @@ -70,6 +70,22 @@ static const int STATUS_INDEX = 2; DEVICE_SETTINGS_STATUS_COLUMN_NAME + " TEXT NOT NULL);"; // clang-format on +std::shared_ptr SQLiteDeviceSettingStorage::createDeviceSettingStorageInterface( + const std::shared_ptr& configurationRoot) { + auto storage = create((*configurationRoot)); + if (!storage) { + ACSDK_ERROR(LX("createDeviceSettingStorageInterfaceFailed").d("reason", "null storage")); + return nullptr; + } + + if (!storage->open()) { + ACSDK_ERROR(LX("createDeviceSettingStorageInterfaceFailed").d("reason", "deviceSettingStorageOpenFailed")); + return nullptr; + } + + return std::move(storage); +} + std::unique_ptr SQLiteDeviceSettingStorage::create( const ConfigurationNode& configurationRoot) { ACSDK_DEBUG5(LX(__func__)); diff --git a/Settings/src/Types/NetworkInfo.cpp b/Settings/src/Types/NetworkInfo.cpp index ae117327a2..847dc0f55d 100644 --- a/Settings/src/Types/NetworkInfo.cpp +++ b/Settings/src/Types/NetworkInfo.cpp @@ -304,6 +304,18 @@ static bool validIpV4(const std::string& input) { return expectedFormat(input, valueChecker, IP_V4_DELIMITER) && (bytes == IP_V4_NUMBER_OF_BYTES); } +/** + * Checks whether the modified hextet and colon position make a valid IPV6 + * @param hextets amount of hextets given + * @param doubleColonPos position of the double colon + * @return + */ +static bool verifyHextetsIPv6(const size_t hextets, const size_t doubleColonPos) { + return ( + (hextets == IP_V6_NUMBER_OF_HEXTETS) || + ((doubleColonPos != std::string::npos) && (hextets < IP_V6_NUMBER_OF_HEXTETS))); +} + /** * Checks whether the input is a valid IPv6 format. * @@ -329,8 +341,7 @@ static bool validIpV6(const std::string& input) { }; if (expectedFormat(no2ColonsInput, valueChecker, IP_V6_DELIMITER, true)) { - return (hextets == IP_V6_NUMBER_OF_HEXTETS) || - ((doubleColonPos != std::string::npos) && (hextets < IP_V6_NUMBER_OF_HEXTETS)); + return verifyHextetsIPv6(hextets, doubleColonPos); } return false; } diff --git a/Settings/test/SettingCallbackAdapterTest.cpp b/Settings/test/SettingCallbackAdapterTest.cpp index f55f810be7..6569d71fb6 100644 --- a/Settings/test/SettingCallbackAdapterTest.cpp +++ b/Settings/test/SettingCallbackAdapterTest.cpp @@ -52,7 +52,7 @@ class TimezoneSettingStub : public TimeZoneSetting { bool clearData(const std::string& value) override; /// Build setting object. - TimezoneSettingStub(const std::string& value); + explicit TimezoneSettingStub(const std::string& value); }; SetSettingResult TimezoneSettingStub::setLocalChange(const std::string& value) { @@ -95,7 +95,9 @@ class SettingCallbackAdapterTest : public Test { void SettingCallbackAdapterTest::SetUp() { auto customerDataManager = std::make_shared(); - m_manager = std::make_shared(customerDataManager); + DeviceSettingManagerSettingConfigurations settingConfigs; + + m_manager = std::make_shared(customerDataManager, settingConfigs); m_timezone = std::make_shared(INIT_TIMEZONE); ASSERT_TRUE(m_manager->addSetting(m_timezone)); } @@ -124,7 +126,7 @@ class ObserverClass { * * @param manager The device manager used to register the callbacks. */ - ObserverClass(std::shared_ptr& manager); + explicit ObserverClass(std::shared_ptr& manager); /** * Destructor. diff --git a/Settings/test/SettingCallbacksTest.cpp b/Settings/test/SettingCallbacksTest.cpp index 44f6edef26..39d935f475 100644 --- a/Settings/test/SettingCallbacksTest.cpp +++ b/Settings/test/SettingCallbacksTest.cpp @@ -62,7 +62,7 @@ class SettingStub : public SettingT { bool clearData(const typename SettingT::ValueType& value); /// Build setting object. - SettingStub(const typename SettingT::ValueType& value); + explicit SettingStub(const typename SettingT::ValueType& value); }; template @@ -113,7 +113,9 @@ class SettingCallbacksTest : public Test { void SettingCallbacksTest::SetUp() { auto customerDataManager = std::make_shared(); - m_manager = std::make_shared(customerDataManager); + DeviceSettingManagerSettingConfigurations settingConfigs; + + m_manager = std::make_shared(customerDataManager, settingConfigs); m_alarmVolumeRamp = std::make_shared>(INIT_ALARM_VOLUME_RAMP); m_wwConfirmation = std::make_shared>(INIT_WAKEWORD_CONFIRMATION); m_timezone = std::make_shared>(INIT_TIMEZONE); @@ -151,7 +153,7 @@ class ObserverClass { * * @param manager The device manager used to register the callbacks. */ - ObserverClass(std::shared_ptr& manager); + explicit ObserverClass(std::shared_ptr& manager); /** * Destructor. diff --git a/Settings/test/Settings/MockSetting.h b/Settings/test/Settings/MockSetting.h index 90917f271f..8f68be116a 100644 --- a/Settings/test/Settings/MockSetting.h +++ b/Settings/test/Settings/MockSetting.h @@ -41,7 +41,7 @@ class MockSetting : public SettingInterface { * * @param value Initial value of this setting. */ - MockSetting(const ValueT& value); + explicit MockSetting(const ValueT& value); }; template diff --git a/Settings/test/SettingsManagerTest.cpp b/Settings/test/SettingsManagerTest.cpp index 9582846eed..a8b952056d 100644 --- a/Settings/test/SettingsManagerTest.cpp +++ b/Settings/test/SettingsManagerTest.cpp @@ -61,7 +61,7 @@ class SettingStub : public SettingInterface { return true; } - SettingStub(const ValueT& value) : SettingInterface{value} { + explicit SettingStub(const ValueT& value) : SettingInterface{value} { } }; @@ -92,7 +92,10 @@ class SettingsManagerTest : public testing::Test { void SettingsManagerTest::SetUp() { auto customerDataManager = std::make_shared(); - m_manager = std::make_shared>(customerDataManager); + std::tuple, SettingConfiguration, SettingConfiguration> + settingConfigs; + m_manager = + std::make_shared>(customerDataManager, settingConfigs); } /// Test add settings and setting the setting value. @@ -108,7 +111,8 @@ TEST_F(SettingsManagerTest, test_setExistingSetting) { /// Test set value for setting that hasn't been registered. TEST_F(SettingsManagerTest, test_setSettingUnavailable) { auto customerDataManager = std::make_shared(); - SettingsManager manager{customerDataManager}; + std::tuple, SettingConfiguration> settingConfigs; + SettingsManager manager{customerDataManager, settingConfigs}; EXPECT_EQ((m_manager->setValue(NEW_INT_VALUE)), SetSettingResult::UNAVAILABLE_SETTING); } diff --git a/SpeechEncoder/src/SpeechEncoder.cpp b/SpeechEncoder/src/SpeechEncoder.cpp index 20ac7d6906..83f2e35bc6 100644 --- a/SpeechEncoder/src/SpeechEncoder.cpp +++ b/SpeechEncoder/src/SpeechEncoder.cpp @@ -185,7 +185,7 @@ void SpeechEncoder::encodeLoop(AudioInputStream::Index begin, AudioInputStream:: // There's still something to send - } else if (writeResult <= 0) { + } else { switch (writeResult) { case AudioInputStream::Writer::Error::WOULDBLOCK: // Should never happen. diff --git a/Storage/SQLiteStorage/include/SQLiteStorage/SQLiteMiscStorage.h b/Storage/SQLiteStorage/include/SQLiteStorage/SQLiteMiscStorage.h index 19eea41924..a9c8a66609 100644 --- a/Storage/SQLiteStorage/include/SQLiteStorage/SQLiteMiscStorage.h +++ b/Storage/SQLiteStorage/include/SQLiteStorage/SQLiteMiscStorage.h @@ -16,6 +16,8 @@ #ifndef ALEXA_CLIENT_SDK_STORAGE_SQLITESTORAGE_INCLUDE_SQLITESTORAGE_SQLITEMISCSTORAGE_H_ #define ALEXA_CLIENT_SDK_STORAGE_SQLITESTORAGE_INCLUDE_SQLITESTORAGE_SQLITEMISCSTORAGE_H_ +#include + #include #include #include @@ -58,54 +60,40 @@ class SQLiteMiscStorage : public avsCommon::sdkInterfaces::storage::MiscStorageI /// @name MiscStorageInterface method overrides. /// @{ bool createDatabase() override; - bool open() override; - bool isOpened() override; - void close() override; - bool createTable( const std::string& componentName, const std::string& tableName, KeyType keyType, ValueType valueType) override; - bool clearTable(const std::string& componentName, const std::string& tableName) override; - bool deleteTable(const std::string& componentName, const std::string& tableName) override; - bool get(const std::string& componentName, const std::string& tableName, const std::string& key, std::string* value) override; - bool add( const std::string& componentName, const std::string& tableName, const std::string& key, const std::string& value) override; - bool update( const std::string& componentName, const std::string& tableName, const std::string& key, const std::string& value) override; - bool put( const std::string& componentName, const std::string& tableName, const std::string& key, const std::string& value) override; - bool remove(const std::string& componentName, const std::string& tableName, const std::string& key) override; - bool tableEntryExists( const std::string& componentName, const std::string& tableName, const std::string& key, bool* tableEntryExistsValue) override; - bool tableExists(const std::string& componentName, const std::string& tableName, bool* tableExistsValue) override; - bool load( const std::string& componentName, const std::string& tableName, @@ -129,7 +117,7 @@ class SQLiteMiscStorage : public avsCommon::sdkInterfaces::storage::MiscStorageI * @param [out] *valueType The value column name. * @return true if the key and value column types were found, else false */ - bool getKeyValueTypes( + bool getKeyValueTypesLocked( const std::string& componentName, const std::string& tableName, KeyType* keyType, @@ -143,7 +131,7 @@ class SQLiteMiscStorage : public avsCommon::sdkInterfaces::storage::MiscStorageI * @param keyType The KeyType that needs to be matched. * @return an error message if the checks fail, else a blank string */ - std::string checkKeyType(const std::string& componentName, const std::string& tableName, KeyType keyType); + std::string checkKeyTypeLocked(const std::string& componentName, const std::string& tableName, KeyType keyType); /** * Helper method that will check value column type. @@ -153,7 +141,10 @@ class SQLiteMiscStorage : public avsCommon::sdkInterfaces::storage::MiscStorageI * @param valueType The ValueType that needs to be matched. * @return an error message if the checks fail, else a blank string */ - std::string checkValueType(const std::string& componentName, const std::string& tableName, ValueType valueType); + std::string checkValueTypeLocked( + const std::string& componentName, + const std::string& tableName, + ValueType valueType); /** * Helper method that will check key and value column type. @@ -164,14 +155,225 @@ class SQLiteMiscStorage : public avsCommon::sdkInterfaces::storage::MiscStorageI * @param valueType The ValueType that needs to be matched. * @return an error message if the checks fail, else a blank string */ - std::string checkKeyValueType( + std::string checkKeyValueTypeLocked( + const std::string& componentName, + const std::string& tableName, + KeyType keyType, + ValueType valueType); + + /** + * Creates a new database. + * If a database is already being handled by this object or there is another internal error, then this function + * returns false. + * + * @note Must be run with @c mutex locked. + * + * @return @c true If the database is created ok, or @c false if a database is already being handled by this object + * or there is a problem creating the database. + */ + bool createDatabaseLocked(); + + /** + * Open an existing database. If this object is already managing an open database, or there is a problem opening + * the database, this function returns false. + * + * @note Must be run with @c mutex locked. + * + * @return @c true If the database is opened ok, @c false if this object is already managing an open database, or if + * there is another internal reason the database could not be opened. + */ + bool openLocked(); + + /** + * Returns true if this object is already managing an open database, false otherwise. + * + * @note Must be run with @c mutex locked. + * + * @return True if this object is already managing an open database, false otherwise. + */ + bool isOpenedLocked(); + + /** + * Close the currently open database, if one is open. + * + * @note Must be run with @c mutex locked. + */ + void closeLocked(); + + /** + * Create a simple key/value pair table. If there is a problem creating + * the table, this function returns false. + * + * @note Must be run with @c mutex locked. + * + * @param componentName The component name. + * @param tableName The table name. + * @param keyType The key type. + * @param valueType The value type. + * @return @c true If the table is created ok, @c false if the table couldn't be created. + */ + bool createTableLocked( const std::string& componentName, const std::string& tableName, KeyType keyType, ValueType valueType); + /** + * Removes all the entries in the table. The table itself will continue to exist. + * + * @note Must be run with @c mutex locked. + * + * @param componentName The component name. + * @param tableName The table name. + * @return @c true If the table is cleared ok, @c false if the table couldn't be cleared. + */ + bool clearTableLocked(const std::string& componentName, const std::string& tableName); + + /** + * Deletes the table. + * The table must be empty before you can delete the table. + * + * @note Must be run with @c mutex locked. + * + * @param componentName The component name. + * @param tableName The table name. + * @return @c true If the table is deleted ok, @c false if the table couldn't be deleted. + */ + bool deleteTableLocked(const std::string& componentName, const std::string& tableName); + + /** + * Gets the value associated with a key in the table. + * + * @note Must be run with @c mutex locked. + * + * @param componentName The component name. + * @param tableName The table name. + * @param key The key for the table entry. + * @param [out] value The value associated with the key in the table. + * @return true If the value was found out ok, @c false if not. + */ + bool getLocked( + const std::string& componentName, + const std::string& tableName, + const std::string& key, + std::string* value); + + /** + * Adds a value in the table. + * + * @note Must be run with @c mutex locked. + * + * @param componentName The component name. + * @param tableName The table name. + * @param key The key for the table entry. + * @param value The value of the table entry. + * @note @c value should be a string literal. + * @return @c true If the value was added ok, @c false if not. + */ + bool addLocked( + const std::string& componentName, + const std::string& tableName, + const std::string& key, + const std::string& value); + + /** + * Updates a value in the table. + * + * @note Must be run with @c mutex locked. + * + * @param tableName The table name. + * @param key The key for the table entry. + * @param value The value of the table entry. + * @note @c value should be a literal string. + * @return @c true If the value was updated ok, @c false if not (including if the entry does not exist). + */ + bool updateLocked( + const std::string& componentName, + const std::string& tableName, + const std::string& key, + const std::string& value); + + /** + * Puts a value in the table. + * Basically, this will add the entry for the key if it doesn't already exist, or it will + * update the entry for the key if it already exists. + * + * @note Must be run with @c mutex locked. + * + * @param componentName The component name. + * @param tableName The table name. + * @param key The key for the table entry. + * @param value The value of the table entry. + * @note @c value should be a literal string. + * @return @c true If the value was put ok, @c false if not. + */ + bool putLocked( + const std::string& componentName, + const std::string& tableName, + const std::string& key, + const std::string& value); + + /** + * Removes a value from the table. + * + * @note Must be run with @c mutex locked. + * + * @param componentName The component name. + * @param tableName The table name. + * @param key The key for the table entry. + * @return @c true If the value was removed ok, @c false if not. + */ + bool removeLocked(const std::string& componentName, const std::string& tableName, const std::string& key); + + /** + * Checks if a key exists in the table. + * + * @note Must be run with @c mutex locked. + * + * @param componentName The component name. + * @param tableName The table name. + * @param key The key for the table entry. + * @param [out] tableEntryExistsValue True if the key exists, @c false if not. + * @return @c true if the table entry's existence was found out ok, else false.. + */ + bool tableEntryExistsLocked( + const std::string& componentName, + const std::string& tableName, + const std::string& key, + bool* tableEntryExistsValue); + + /** + * Checks if a table exists in the DB. + * + * @note Must be run with @c mutex locked. + * + * @param componentName The component name. + * @param tableName The table name. + * @param [out] tableExistsValue True if the table exists, @c false if not. + * @return @c true if the table's existence was found out ok, else false. + */ + bool tableExistsLocked(const std::string& componentName, const std::string& tableName, bool* tableExistsValue); + + /** + * Loads the table entries into a map. + * + * @note Must be run with @c mutex locked. + * + * @param componentName The component name. + * @param tableName The table name. + * @param [out] valueContainer The container for the values in the table. + * @return @c true If the values were loaded ok, @c false if not. + */ + bool loadLocked( + const std::string& componentName, + const std::string& tableName, + std::unordered_map* valueContainer); + /// The underlying database class. alexaClientSDK::storage::sqliteStorage::SQLiteDatabase m_db; + + /// This is the mutex to serialize access to @c m_db. + std::mutex m_mutex; }; } // namespace sqliteStorage diff --git a/Storage/SQLiteStorage/src/SQLiteMiscStorage.cpp b/Storage/SQLiteStorage/src/SQLiteMiscStorage.cpp index 0817f2abcf..f1f235a564 100644 --- a/Storage/SQLiteStorage/src/SQLiteMiscStorage.cpp +++ b/Storage/SQLiteStorage/src/SQLiteMiscStorage.cpp @@ -71,7 +71,10 @@ static const bool CHECK_TABLE_NOT_EXISTS = false; * @param tableName The table name to check. * @return an error message if the checks fail, else a blank string */ -static std::string basicDBChecks(SQLiteDatabase& db, const std::string& componentName, const std::string& tableName); +static std::string basicDBChecksLocked( + SQLiteDatabase& db, + const std::string& componentName, + const std::string& tableName); /** * Helper method that will check basic things about the DB. @@ -82,7 +85,7 @@ static std::string basicDBChecks(SQLiteDatabase& db, const std::string& componen * @param tableShouldExist If true, checks if the table should exist. If false, it checks the opposite. * @return an error message if the checks fail, else a blank string */ -static std::string basicDBChecks( +static std::string basicDBChecksLocked( SQLiteDatabase& db, const std::string& componentName, const std::string& tableName, @@ -158,24 +161,37 @@ SQLiteMiscStorage::~SQLiteMiscStorage() { } bool SQLiteMiscStorage::open() { + std::lock_guard lock(m_mutex); + return openLocked(); +} + +bool SQLiteMiscStorage::openLocked() { if (!m_db.open()) { ACSDK_DEBUG0(LX("openDatabaseFailed")); return false; } - return true; } void SQLiteMiscStorage::close() { + std::lock_guard lock(m_mutex); + return closeLocked(); +} + +void SQLiteMiscStorage::closeLocked() { m_db.close(); } bool SQLiteMiscStorage::createDatabase() { + std::lock_guard lock(m_mutex); + return createDatabaseLocked(); +} + +bool SQLiteMiscStorage::createDatabaseLocked() { if (!m_db.initialize()) { ACSDK_ERROR(LX("createDatabaseFailed")); return false; } - return true; } @@ -198,7 +214,7 @@ std::string getDBTableName(const std::string& componentName, const std::string& return (componentName + MISC_DATABASE_DB_COMPONENT_TABLE_NAMES_SEPARATOR + tableName); } -std::string basicDBChecks(SQLiteDatabase& db, const std::string& componentName, const std::string& tableName) { +std::string basicDBChecksLocked(SQLiteDatabase& db, const std::string& componentName, const std::string& tableName) { if (!db.isDatabaseReady()) { return "Database is not ready"; } @@ -214,12 +230,12 @@ std::string basicDBChecks(SQLiteDatabase& db, const std::string& componentName, return ""; } -std::string basicDBChecks( +std::string basicDBChecksLocked( SQLiteDatabase& db, const std::string& componentName, const std::string& tableName, bool tableShouldExist) { - const std::string errorReason = basicDBChecks(db, componentName, tableName); + const std::string errorReason = basicDBChecksLocked(db, componentName, tableName); if (!errorReason.empty()) { return errorReason; } @@ -266,13 +282,13 @@ std::string getDBDataType(const std::string& keyValueType) { return ""; } -bool SQLiteMiscStorage::getKeyValueTypes( +bool SQLiteMiscStorage::getKeyValueTypesLocked( const std::string& componentName, const std::string& tableName, KeyType* keyType, ValueType* valueType) { const std::string errorEvent = "getKeyValueTypesFailed"; - const std::string errorReason = basicDBChecks(m_db, componentName, tableName, CHECK_TABLE_EXISTS); + const std::string errorReason = basicDBChecksLocked(m_db, componentName, tableName, CHECK_TABLE_EXISTS); if (!errorReason.empty()) { ACSDK_ERROR(LX(errorEvent).m(errorReason)); @@ -341,7 +357,7 @@ bool SQLiteMiscStorage::getKeyValueTypes( return true; } -std::string SQLiteMiscStorage::checkKeyType( +std::string SQLiteMiscStorage::checkKeyTypeLocked( const std::string& componentName, const std::string& tableName, KeyType keyType) { @@ -349,7 +365,7 @@ std::string SQLiteMiscStorage::checkKeyType( return "Cannot check for unknown key column type"; } - const std::string basicDBChecksError = basicDBChecks(m_db, componentName, tableName, CHECK_TABLE_EXISTS); + const std::string basicDBChecksError = basicDBChecksLocked(m_db, componentName, tableName, CHECK_TABLE_EXISTS); if (!basicDBChecksError.empty()) { return basicDBChecksError; } @@ -357,7 +373,7 @@ std::string SQLiteMiscStorage::checkKeyType( KeyType keyColumnType; ValueType valueColumnType; - if (!getKeyValueTypes(componentName, tableName, &keyColumnType, &valueColumnType)) { + if (!getKeyValueTypesLocked(componentName, tableName, &keyColumnType, &valueColumnType)) { return "Unable to get key column type"; } @@ -372,7 +388,7 @@ std::string SQLiteMiscStorage::checkKeyType( return ""; } -std::string SQLiteMiscStorage::checkValueType( +std::string SQLiteMiscStorage::checkValueTypeLocked( const std::string& componentName, const std::string& tableName, ValueType valueType) { @@ -380,7 +396,7 @@ std::string SQLiteMiscStorage::checkValueType( return "Cannot check for unknown value column type"; } - const std::string basicDBChecksError = basicDBChecks(m_db, componentName, tableName, CHECK_TABLE_EXISTS); + const std::string basicDBChecksError = basicDBChecksLocked(m_db, componentName, tableName, CHECK_TABLE_EXISTS); if (!basicDBChecksError.empty()) { return basicDBChecksError; } @@ -388,7 +404,7 @@ std::string SQLiteMiscStorage::checkValueType( KeyType keyColumnType; ValueType valueColumnType; - if (!getKeyValueTypes(componentName, tableName, &keyColumnType, &valueColumnType)) { + if (!getKeyValueTypesLocked(componentName, tableName, &keyColumnType, &valueColumnType)) { return "Unable to get value column type"; } @@ -403,7 +419,7 @@ std::string SQLiteMiscStorage::checkValueType( return ""; } -std::string SQLiteMiscStorage::checkKeyValueType( +std::string SQLiteMiscStorage::checkKeyValueTypeLocked( const std::string& componentName, const std::string& tableName, KeyType keyType, @@ -415,7 +431,7 @@ std::string SQLiteMiscStorage::checkKeyValueType( return "Cannot check for unknown value column type"; } - const std::string basicDBChecksError = basicDBChecks(m_db, componentName, tableName, CHECK_TABLE_EXISTS); + const std::string basicDBChecksError = basicDBChecksLocked(m_db, componentName, tableName, CHECK_TABLE_EXISTS); if (!basicDBChecksError.empty()) { return basicDBChecksError; } @@ -423,7 +439,7 @@ std::string SQLiteMiscStorage::checkKeyValueType( KeyType keyColumnType; ValueType valueColumnType; - if (!getKeyValueTypes(componentName, tableName, &keyColumnType, &valueColumnType)) { + if (!getKeyValueTypesLocked(componentName, tableName, &keyColumnType, &valueColumnType)) { return "Unable to get key/value column types"; } @@ -445,12 +461,21 @@ std::string SQLiteMiscStorage::checkKeyValueType( } bool SQLiteMiscStorage::createTable( + const std::string& componentName, + const std::string& tableName, + KeyType keyType, + ValueType valueType) { + std::lock_guard lock(m_mutex); + return createTableLocked(componentName, tableName, keyType, valueType); +} + +bool SQLiteMiscStorage::createTableLocked( const std::string& componentName, const std::string& tableName, KeyType keyType, ValueType valueType) { const std::string errorEvent = "createTableFailed"; - const std::string errorReason = basicDBChecks(m_db, componentName, tableName, CHECK_TABLE_NOT_EXISTS); + const std::string errorReason = basicDBChecksLocked(m_db, componentName, tableName, CHECK_TABLE_NOT_EXISTS); if (!errorReason.empty()) { ACSDK_ERROR(LX(errorEvent).m(errorReason)); @@ -483,8 +508,13 @@ bool SQLiteMiscStorage::createTable( } bool SQLiteMiscStorage::clearTable(const std::string& componentName, const std::string& tableName) { + std::lock_guard lock(m_mutex); + return clearTableLocked(componentName, tableName); +} + +bool SQLiteMiscStorage::clearTableLocked(const std::string& componentName, const std::string& tableName) { const std::string errorEvent = "clearTableFailed"; - const std::string errorReason = basicDBChecks(m_db, componentName, tableName, CHECK_TABLE_EXISTS); + const std::string errorReason = basicDBChecksLocked(m_db, componentName, tableName, CHECK_TABLE_EXISTS); if (!errorReason.empty()) { ACSDK_ERROR(LX(errorEvent).m(errorReason)); @@ -502,8 +532,13 @@ bool SQLiteMiscStorage::clearTable(const std::string& componentName, const std:: } bool SQLiteMiscStorage::deleteTable(const std::string& componentName, const std::string& tableName) { + std::lock_guard lock(m_mutex); + return deleteTableLocked(componentName, tableName); +} + +bool SQLiteMiscStorage::deleteTableLocked(const std::string& componentName, const std::string& tableName) { const std::string errorEvent = "deleteTableFailed"; - const std::string errorReason = basicDBChecks(m_db, componentName, tableName, CHECK_TABLE_EXISTS); + const std::string errorReason = basicDBChecksLocked(m_db, componentName, tableName, CHECK_TABLE_EXISTS); if (!errorReason.empty()) { ACSDK_ERROR(LX(errorEvent).m(errorReason)); @@ -533,6 +568,15 @@ bool SQLiteMiscStorage::deleteTable(const std::string& componentName, const std: } bool SQLiteMiscStorage::get( + const std::string& componentName, + const std::string& tableName, + const std::string& key, + std::string* value) { + std::lock_guard lock(m_mutex); + return getLocked(componentName, tableName, key, value); +} + +bool SQLiteMiscStorage::getLocked( const std::string& componentName, const std::string& tableName, const std::string& key, @@ -544,7 +588,7 @@ bool SQLiteMiscStorage::get( return false; } - const std::string errorReason = basicDBChecks(m_db, componentName, tableName, CHECK_TABLE_EXISTS); + const std::string errorReason = basicDBChecksLocked(m_db, componentName, tableName, CHECK_TABLE_EXISTS); if (!errorReason.empty()) { ACSDK_ERROR(LX(errorEvent).m(errorReason)); return false; @@ -552,7 +596,7 @@ bool SQLiteMiscStorage::get( std::string dbTableName = getDBTableName(componentName, tableName); - const std::string keyTypeError = checkKeyType(componentName, tableName, KeyType::STRING_KEY); + const std::string keyTypeError = checkKeyTypeLocked(componentName, tableName, KeyType::STRING_KEY); if (!keyTypeError.empty()) { ACSDK_ERROR(LX(errorEvent).m(keyTypeError)); return false; @@ -586,6 +630,15 @@ bool SQLiteMiscStorage::get( } bool SQLiteMiscStorage::tableEntryExists( + const std::string& componentName, + const std::string& tableName, + const std::string& key, + bool* tableEntryExistsValue) { + std::lock_guard lock(m_mutex); + return tableEntryExistsLocked(componentName, tableName, key, tableEntryExistsValue); +} + +bool SQLiteMiscStorage::tableEntryExistsLocked( const std::string& componentName, const std::string& tableName, const std::string& key, @@ -597,7 +650,7 @@ bool SQLiteMiscStorage::tableEntryExists( return false; } - const std::string errorReason = basicDBChecks(m_db, componentName, tableName, CHECK_TABLE_EXISTS); + const std::string errorReason = basicDBChecksLocked(m_db, componentName, tableName, CHECK_TABLE_EXISTS); if (!errorReason.empty()) { ACSDK_ERROR(LX(errorEvent).m(errorReason)); return false; @@ -605,7 +658,7 @@ bool SQLiteMiscStorage::tableEntryExists( KeyType keyColumnType; ValueType valueColumnType; - if (!getKeyValueTypes(componentName, tableName, &keyColumnType, &valueColumnType)) { + if (!getKeyValueTypesLocked(componentName, tableName, &keyColumnType, &valueColumnType)) { ACSDK_ERROR(LX(errorEvent).m("Unable to get key/value column types")); return false; } @@ -616,7 +669,7 @@ bool SQLiteMiscStorage::tableEntryExists( if (valueColumnType == ValueType::STRING_VALUE) { std::string tableEntry; - if (!get(componentName, tableName, key, &tableEntry)) { + if (!getLocked(componentName, tableName, key, &tableEntry)) { ACSDK_ERROR(LX(errorEvent).m("Unable to get table entry")); return false; } @@ -631,14 +684,21 @@ bool SQLiteMiscStorage::tableExists( const std::string& componentName, const std::string& tableName, bool* tableExistsValue) { - const std::string errorEvent = "tableExistsFailed"; + std::lock_guard lock(m_mutex); + return tableExistsLocked(componentName, tableName, tableExistsValue); +} +bool SQLiteMiscStorage::tableExistsLocked( + const std::string& componentName, + const std::string& tableName, + bool* tableExistsValue) { + const std::string errorEvent = "tableExistsFailed"; if (!tableExistsValue) { ACSDK_ERROR(LX(errorEvent).m("tableExistsValue is nullptr.")); return false; } - const std::string errorReason = basicDBChecks(m_db, componentName, tableName); + const std::string errorReason = basicDBChecksLocked(m_db, componentName, tableName); if (!errorReason.empty()) { ACSDK_ERROR(LX(errorEvent).m(errorReason)); return false; @@ -650,12 +710,21 @@ bool SQLiteMiscStorage::tableExists( } bool SQLiteMiscStorage::add( + const std::string& componentName, + const std::string& tableName, + const std::string& key, + const std::string& value) { + std::lock_guard lock(m_mutex); + return addLocked(componentName, tableName, key, value); +} + +bool SQLiteMiscStorage::addLocked( const std::string& componentName, const std::string& tableName, const std::string& key, const std::string& value) { const std::string errorEvent = "addToTableFailed"; - const std::string errorReason = basicDBChecks(m_db, componentName, tableName, CHECK_TABLE_EXISTS); + const std::string errorReason = basicDBChecksLocked(m_db, componentName, tableName, CHECK_TABLE_EXISTS); if (!errorReason.empty()) { ACSDK_ERROR(LX(errorEvent).m(errorReason)); @@ -663,14 +732,14 @@ bool SQLiteMiscStorage::add( } const std::string keyValueTypeError = - checkKeyValueType(componentName, tableName, KeyType::STRING_KEY, ValueType::STRING_VALUE); + checkKeyValueTypeLocked(componentName, tableName, KeyType::STRING_KEY, ValueType::STRING_VALUE); if (!keyValueTypeError.empty()) { ACSDK_ERROR(LX(errorEvent).m(keyValueTypeError)); return false; } bool tableEntryExistsValue; - if (!tableEntryExists(componentName, tableName, key, &tableEntryExistsValue)) { + if (!tableEntryExistsLocked(componentName, tableName, key, &tableEntryExistsValue)) { ACSDK_ERROR(LX(errorEvent).d("Unable to get table entry information for " + key + " in table", tableName)); return false; } @@ -707,12 +776,21 @@ bool SQLiteMiscStorage::add( } bool SQLiteMiscStorage::update( + const std::string& componentName, + const std::string& tableName, + const std::string& key, + const std::string& value) { + std::lock_guard lock(m_mutex); + return updateLocked(componentName, tableName, key, value); +} + +bool SQLiteMiscStorage::updateLocked( const std::string& componentName, const std::string& tableName, const std::string& key, const std::string& value) { const std::string errorEvent = "updateTableEntryFailed"; - const std::string errorReason = basicDBChecks(m_db, componentName, tableName, CHECK_TABLE_EXISTS); + const std::string errorReason = basicDBChecksLocked(m_db, componentName, tableName, CHECK_TABLE_EXISTS); if (!errorReason.empty()) { ACSDK_ERROR(LX(errorEvent).m(errorReason)); @@ -720,14 +798,14 @@ bool SQLiteMiscStorage::update( } const std::string keyValueTypeError = - checkKeyValueType(componentName, tableName, KeyType::STRING_KEY, ValueType::STRING_VALUE); + checkKeyValueTypeLocked(componentName, tableName, KeyType::STRING_KEY, ValueType::STRING_VALUE); if (!keyValueTypeError.empty()) { ACSDK_ERROR(LX(errorEvent).m(keyValueTypeError)); return false; } bool tableEntryExistsValue; - if (!tableEntryExists(componentName, tableName, key, &tableEntryExistsValue)) { + if (!tableEntryExistsLocked(componentName, tableName, key, &tableEntryExistsValue)) { ACSDK_ERROR(LX(errorEvent).d("Unable to get table entry information for " + key + " in table", tableName)); return false; } @@ -764,12 +842,21 @@ bool SQLiteMiscStorage::update( } bool SQLiteMiscStorage::put( + const std::string& componentName, + const std::string& tableName, + const std::string& key, + const std::string& value) { + std::lock_guard lock(m_mutex); + return putLocked(componentName, tableName, key, value); +} + +bool SQLiteMiscStorage::putLocked( const std::string& componentName, const std::string& tableName, const std::string& key, const std::string& value) { const std::string errorEvent = "putToTableFailed"; - const std::string errorReason = basicDBChecks(m_db, componentName, tableName, CHECK_TABLE_EXISTS); + const std::string errorReason = basicDBChecksLocked(m_db, componentName, tableName, CHECK_TABLE_EXISTS); if (!errorReason.empty()) { ACSDK_ERROR(LX(errorEvent).m(errorReason)); @@ -777,14 +864,14 @@ bool SQLiteMiscStorage::put( } const std::string keyValueTypeError = - checkKeyValueType(componentName, tableName, KeyType::STRING_KEY, ValueType::STRING_VALUE); + checkKeyValueTypeLocked(componentName, tableName, KeyType::STRING_KEY, ValueType::STRING_VALUE); if (!keyValueTypeError.empty()) { ACSDK_ERROR(LX(errorEvent).m(keyValueTypeError)); return false; } bool tableEntryExistsValue; - if (!tableEntryExists(componentName, tableName, key, &tableEntryExistsValue)) { + if (!tableEntryExistsLocked(componentName, tableName, key, &tableEntryExistsValue)) { ACSDK_ERROR(LX(errorEvent).d("Unable to get table entry information for " + key + " in table", tableName)); return false; } @@ -826,22 +913,30 @@ bool SQLiteMiscStorage::put( } bool SQLiteMiscStorage::remove(const std::string& componentName, const std::string& tableName, const std::string& key) { + std::lock_guard lock(m_mutex); + return removeLocked(componentName, tableName, key); +} + +bool SQLiteMiscStorage::removeLocked( + const std::string& componentName, + const std::string& tableName, + const std::string& key) { const std::string errorEvent = "removeTableEntryFailed"; - const std::string errorReason = basicDBChecks(m_db, componentName, tableName, CHECK_TABLE_EXISTS); + const std::string errorReason = basicDBChecksLocked(m_db, componentName, tableName, CHECK_TABLE_EXISTS); if (!errorReason.empty()) { ACSDK_ERROR(LX(errorEvent).m(errorReason)); return false; } - const std::string keyTypeError = checkKeyType(componentName, tableName, KeyType::STRING_KEY); + const std::string keyTypeError = checkKeyTypeLocked(componentName, tableName, KeyType::STRING_KEY); if (!keyTypeError.empty()) { ACSDK_ERROR(LX(errorEvent).m(keyTypeError)); return false; } bool tableEntryExistsValue; - if (!tableEntryExists(componentName, tableName, key, &tableEntryExistsValue)) { + if (!tableEntryExistsLocked(componentName, tableName, key, &tableEntryExistsValue)) { ACSDK_ERROR(LX(errorEvent).d("Unable to get table entry information for " + key + " in table", tableName)); return false; } @@ -876,11 +971,19 @@ bool SQLiteMiscStorage::remove(const std::string& componentName, const std::stri } bool SQLiteMiscStorage::load( + const std::string& componentName, + const std::string& tableName, + std::unordered_map* valueContainer) { + std::lock_guard lock(m_mutex); + return loadLocked(componentName, tableName, valueContainer); +} + +bool SQLiteMiscStorage::loadLocked( const std::string& componentName, const std::string& tableName, std::unordered_map* valueContainer) { const std::string errorEvent = "loadFromTableFailed"; - const std::string errorReason = basicDBChecks(m_db, componentName, tableName, CHECK_TABLE_EXISTS); + const std::string errorReason = basicDBChecksLocked(m_db, componentName, tableName, CHECK_TABLE_EXISTS); if (!errorReason.empty()) { ACSDK_ERROR(LX(errorEvent).m(errorReason)); @@ -893,7 +996,7 @@ bool SQLiteMiscStorage::load( } const std::string keyValueTypeError = - checkKeyValueType(componentName, tableName, KeyType::STRING_KEY, ValueType::STRING_VALUE); + checkKeyValueTypeLocked(componentName, tableName, KeyType::STRING_KEY, ValueType::STRING_VALUE); if (!keyValueTypeError.empty()) { ACSDK_ERROR(LX(errorEvent).m(keyValueTypeError)); return false; @@ -940,6 +1043,11 @@ bool SQLiteMiscStorage::load( } bool SQLiteMiscStorage::isOpened() { + std::lock_guard lock(m_mutex); + return isOpenedLocked(); +} + +bool SQLiteMiscStorage::isOpenedLocked() { return m_db.isDatabaseReady(); } diff --git a/Storage/SQLiteStorage/test/SQLiteMiscStorageTest.cpp b/Storage/SQLiteStorage/test/SQLiteMiscStorageTest.cpp index a6cc850f64..10adc7b7bf 100644 --- a/Storage/SQLiteStorage/test/SQLiteMiscStorageTest.cpp +++ b/Storage/SQLiteStorage/test/SQLiteMiscStorageTest.cpp @@ -12,6 +12,7 @@ * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ +#include #include @@ -93,11 +94,11 @@ void SQLiteMiscStorageTest::createTestTable( const SQLiteMiscStorage::ValueType valueType) { if (m_miscStorage) { bool tableExists; - m_miscStorage->tableExists(COMPONENT_NAME, tableName, &tableExists); + ASSERT_TRUE(m_miscStorage->tableExists(COMPONENT_NAME, tableName, &tableExists)); if (tableExists) { - m_miscStorage->clearTable(COMPONENT_NAME, tableName); + ASSERT_TRUE(m_miscStorage->clearTable(COMPONENT_NAME, tableName)); } else { - m_miscStorage->createTable(COMPONENT_NAME, tableName, keyType, valueType); + ASSERT_TRUE(m_miscStorage->createTable(COMPONENT_NAME, tableName, keyType, valueType)); } } } @@ -105,10 +106,10 @@ void SQLiteMiscStorageTest::createTestTable( void SQLiteMiscStorageTest::deleteTestTable(const std::string& tableName) { if (m_miscStorage) { bool tableExists; - m_miscStorage->tableExists(COMPONENT_NAME, tableName, &tableExists); + ASSERT_TRUE(m_miscStorage->tableExists(COMPONENT_NAME, tableName, &tableExists)); if (tableExists) { - m_miscStorage->clearTable(COMPONENT_NAME, tableName); - m_miscStorage->deleteTable(COMPONENT_NAME, tableName); + ASSERT_TRUE(m_miscStorage->clearTable(COMPONENT_NAME, tableName)); + ASSERT_TRUE(m_miscStorage->deleteTable(COMPONENT_NAME, tableName)); } } } @@ -435,6 +436,73 @@ TEST_F(SQLiteMiscStorageTest, test_escapeSingleQuoteCharacters) { deleteTestTable(tableName); } +/// Tests with table entry add, remove, update, put in multiple threads +TEST_F(SQLiteMiscStorageTest, test_tableEntryTestsMultiThread) { + const int NUMBER_OF_THREADS = 16; + std::thread threads[NUMBER_OF_THREADS]; + for (int threadIndex = 0; threadIndex < NUMBER_OF_THREADS; ++threadIndex) { + threads[threadIndex] = std::thread([this, threadIndex]() { + std::string tableName = "SQLiteMiscStorageTableEntryTest" + std::to_string(threadIndex); + std::string tableEntryKey = "tableEntryTestsKey" + std::to_string(threadIndex); + std::string tableEntryAddedValue = "tableEntryAddedValue" + std::to_string(threadIndex); + std::string tableEntryPutValue = "tableEntryPutValue" + std::to_string(threadIndex); + std::string tableEntryAnotherPutValue = "tableEntryAnotherPutValue" + std::to_string(threadIndex); + std::string tableEntryUpdatedValue = "tableEntryUpdatedValue" + std::to_string(threadIndex); + std::string tableEntryValue; + deleteTestTable(tableName); + + createTestTable( + tableName, SQLiteMiscStorage::KeyType::STRING_KEY, SQLiteMiscStorage::ValueType::STRING_VALUE); + + /// Entry doesn't exist at first + bool tableEntryExists; + ASSERT_TRUE(m_miscStorage->tableEntryExists(COMPONENT_NAME, tableName, tableEntryKey, &tableEntryExists)); + ASSERT_FALSE(tableEntryExists); + + /// Ensure that add entry works + ASSERT_TRUE(m_miscStorage->add(COMPONENT_NAME, tableName, tableEntryKey, tableEntryAddedValue)); + ASSERT_TRUE(m_miscStorage->tableEntryExists(COMPONENT_NAME, tableName, tableEntryKey, &tableEntryExists)); + ASSERT_TRUE(tableEntryExists); + ASSERT_TRUE(m_miscStorage->get(COMPONENT_NAME, tableName, tableEntryKey, &tableEntryValue)); + ASSERT_EQ(tableEntryValue, tableEntryAddedValue); + + /// Ensure that update entry works + ASSERT_TRUE(m_miscStorage->update(COMPONENT_NAME, tableName, tableEntryKey, tableEntryUpdatedValue)); + ASSERT_TRUE(m_miscStorage->tableEntryExists(COMPONENT_NAME, tableName, tableEntryKey, &tableEntryExists)); + ASSERT_TRUE(tableEntryExists); + ASSERT_TRUE(m_miscStorage->get(COMPONENT_NAME, tableName, tableEntryKey, &tableEntryValue)); + ASSERT_EQ(tableEntryValue, tableEntryUpdatedValue); + + /// Ensure that remove entry works + ASSERT_TRUE(m_miscStorage->tableEntryExists(COMPONENT_NAME, tableName, tableEntryKey, &tableEntryExists)); + ASSERT_TRUE(tableEntryExists); + ASSERT_TRUE(m_miscStorage->remove(COMPONENT_NAME, tableName, tableEntryKey)); + ASSERT_TRUE(m_miscStorage->tableEntryExists(COMPONENT_NAME, tableName, tableEntryKey, &tableEntryExists)); + ASSERT_FALSE(tableEntryExists); + + /// Ensure that put entry works + /// Try with a new entry for key + ASSERT_TRUE(m_miscStorage->put(COMPONENT_NAME, tableName, tableEntryKey, tableEntryPutValue)); + ASSERT_TRUE(m_miscStorage->tableEntryExists(COMPONENT_NAME, tableName, tableEntryKey, &tableEntryExists)); + ASSERT_TRUE(tableEntryExists); + ASSERT_TRUE(m_miscStorage->get(COMPONENT_NAME, tableName, tableEntryKey, &tableEntryValue)); + ASSERT_EQ(tableEntryValue, tableEntryPutValue); + /// Try with an existing entry for key + ASSERT_TRUE(m_miscStorage->put(COMPONENT_NAME, tableName, tableEntryKey, tableEntryAnotherPutValue)); + ASSERT_TRUE(m_miscStorage->tableEntryExists(COMPONENT_NAME, tableName, tableEntryKey, &tableEntryExists)); + ASSERT_TRUE(tableEntryExists); + ASSERT_TRUE(m_miscStorage->get(COMPONENT_NAME, tableName, tableEntryKey, &tableEntryValue)); + ASSERT_EQ(tableEntryValue, tableEntryAnotherPutValue); + + deleteTestTable(tableName); + }); + } + + for (int threadIndex = 0; threadIndex < NUMBER_OF_THREADS; ++threadIndex) { + threads[threadIndex].join(); + } +} + } // namespace test } // namespace sqliteStorage } // namespace storage diff --git a/SynchronizeStateSender/include/SynchronizeStateSender/PostConnectSynchronizeStateSender.h b/SynchronizeStateSender/include/SynchronizeStateSender/PostConnectSynchronizeStateSender.h index 1f880d3c11..845c2190a7 100644 --- a/SynchronizeStateSender/include/SynchronizeStateSender/PostConnectSynchronizeStateSender.h +++ b/SynchronizeStateSender/include/SynchronizeStateSender/PostConnectSynchronizeStateSender.h @@ -24,6 +24,7 @@ #include #include #include +#include #include namespace alexaClientSDK { @@ -41,10 +42,12 @@ class PostConnectSynchronizeStateSender * Creates a new instance of the @c PostConnectSynchronizeStateSender. * * @param contextManager The @c ContextManager to request the context from. + * @param metricRecorder The object used for metric recording. * @return a new instance of the @c PostConnectSynchronizeStateSender. */ static std::shared_ptr create( - std::shared_ptr contextManager); + std::shared_ptr contextManager, + std::shared_ptr metricRecorder = nullptr); /// ContextRequesterInterface Methods. /// @{ @@ -63,9 +66,11 @@ class PostConnectSynchronizeStateSender /** * Constructor. * @param contextManager + * @param metricRecorder */ PostConnectSynchronizeStateSender( - std::shared_ptr contextManager); + std::shared_ptr contextManager, + std::shared_ptr metricRecorder); /** * A method to fetch the context and store it in @c m_contextString. @@ -83,6 +88,9 @@ class PostConnectSynchronizeStateSender /// The @c ContextManagerInterface to request the context from. std::shared_ptr m_contextManager; + /// The @c MetricRecorderInterface to record metrics with. + std::shared_ptr m_metricRecorder; + /// Flag to indicate the PostConnectOperation is stopping. bool m_isStopping; diff --git a/SynchronizeStateSender/include/SynchronizeStateSender/SynchronizeStateSenderFactory.h b/SynchronizeStateSender/include/SynchronizeStateSender/SynchronizeStateSenderFactory.h index 0c7aa71dd2..9db521ae38 100644 --- a/SynchronizeStateSender/include/SynchronizeStateSender/SynchronizeStateSenderFactory.h +++ b/SynchronizeStateSender/include/SynchronizeStateSender/SynchronizeStateSenderFactory.h @@ -21,6 +21,7 @@ #include #include #include +#include namespace alexaClientSDK { namespace synchronizeStateSender { @@ -34,6 +35,7 @@ class SynchronizeStateSenderFactory : public avsCommon::sdkInterfaces::PostConne * Creates a new instance of the @c PostConnectOperationProviderInterface. * * @param contextManager The @c ContextManager used to construct the synchronize state sender. + * @param metricRecorder The object used for metric recording. * @return a new instance of the @c SynchronizeStateSenderFactory. */ static std::shared_ptr @@ -41,7 +43,8 @@ class SynchronizeStateSenderFactory : public avsCommon::sdkInterfaces::PostConne const std::shared_ptr< acsdkPostConnectOperationProviderRegistrarInterfaces::PostConnectOperationProviderRegistrarInterface>& providerRegistrar, - const std::shared_ptr& contextManager); + const std::shared_ptr& contextManager, + const std::shared_ptr& metricRecorder = nullptr); /** * Creates a new instance of the @c SynchronizeStateSenderFactory. @@ -51,7 +54,8 @@ class SynchronizeStateSenderFactory : public avsCommon::sdkInterfaces::PostConne * @return a new instance of the @c SynchronizeStateSenderFactory. */ static std::shared_ptr create( - std::shared_ptr contextManager); + std::shared_ptr contextManager, + std::shared_ptr metricRecorder = nullptr); /// ContextRequesterInterface Methods. /// @{ @@ -63,11 +67,17 @@ class SynchronizeStateSenderFactory : public avsCommon::sdkInterfaces::PostConne * Constructor. * * @param contextManager The @c ContextManager used to construct the synchronize state sender. + * @param metricRecorder The object used for metric recording. */ - SynchronizeStateSenderFactory(std::shared_ptr contextManager); + SynchronizeStateSenderFactory( + std::shared_ptr contextManager, + std::shared_ptr metricRecorder); /// The @c ContextManager used in the construction of the @c PostConnectSynchronizeStateSender. std::shared_ptr m_contextManager; + + /// @param metricRecorder The object used for metric recording. + std::shared_ptr m_metricRecorder; }; } // namespace synchronizeStateSender diff --git a/SynchronizeStateSender/src/PostConnectSynchronizeStateSender.cpp b/SynchronizeStateSender/src/PostConnectSynchronizeStateSender.cpp index 5063b2341e..eb17f822c6 100644 --- a/SynchronizeStateSender/src/PostConnectSynchronizeStateSender.cpp +++ b/SynchronizeStateSender/src/PostConnectSynchronizeStateSender.cpp @@ -19,11 +19,18 @@ #include #include +#include +#include +#include +#include +#include + namespace alexaClientSDK { namespace synchronizeStateSender { using namespace avsCommon::avs; using namespace avsCommon::sdkInterfaces; +using namespace avsCommon::utils::metrics; /// String to identify log entries originating from this file. static const std::string TAG("PostConnectSynchronizeStateSender"); @@ -41,16 +48,49 @@ static const std::string SYNCHRONIZE_STATE_NAMESPACE = "System"; /// The name of the SynchronizeState event. static const std::string SYNCHRONIZE_STATE_NAME = "SynchronizeState"; +/// Metric Activity Name Prefix for PostConnectSynchronizeStateSender metric source +static const std::string METRIC_ACTIVITY_NAME_PREFIX = "POSTCONNECT_SYNCHRONIZE_STATE_SENDER-"; + /// Table with the retry times on subsequent retries. static const std::vector RETRY_TABLE = { - 250, // Retry 1: 0.25s - 1000, // Retry 2: 1.00s - 3000, // Retry 3: 3.00s - 5000, // Retry 4: 5.00s - 10000, // Retry 5: 10.00s - 20000, // Retry 6: 20.00s + 250, // Retry 1: 0.25s + 1000, // Retry 1: 1s + 2000, // Retry 2: 2s + 4000, // Retry 3 4s + 8000, // Retry 4: 8s + 16000, // Retry 5: 16s + 32000, // Retry 6: 32s + 64000, // Retry 7: 64s + 128000, // Retry 8: 128s + 256000 // Retry 9: 256s }; +/** + * Handles a Metric event by creating and recording it. Failure to create or record the event results + * in an early return. + * + * @param metricRecorder The @c MetricRecorderInterface which records Metric events. + * @param eventName The activity name of the Metric event. + * @param reason Additional information on cause of Metric event. + */ +static void submitMetric( + const std::shared_ptr& metricRecorder, + const std::string& eventName, + const std::string& reason) { + auto metricEventBuilder = MetricEventBuilder{} + .setActivityName(METRIC_ACTIVITY_NAME_PREFIX + eventName) + .addDataPoint(DataPointCounterBuilder{}.setName(eventName).increment(1).build()) + .addDataPoint(DataPointStringBuilder{}.setName("REASON").setValue(reason).build()); + + auto metricEvent = metricEventBuilder.build(); + + if (metricEvent == nullptr) { + ACSDK_ERROR(LX("Error creating metric.")); + return; + } + recordMetric(metricRecorder, metricEvent); +} + /// The instance of the @c RetryTimer used to calculate retry backoff times. static avsCommon::utils::RetryTimer RETRY_TIMER{RETRY_TABLE}; @@ -58,19 +98,24 @@ static avsCommon::utils::RetryTimer RETRY_TIMER{RETRY_TABLE}; std::chrono::milliseconds CONTEXT_FETCH_TIMEOUT = std::chrono::milliseconds(2000); std::shared_ptr PostConnectSynchronizeStateSender::create( - std::shared_ptr contextManager) { + std::shared_ptr contextManager, + std::shared_ptr metricRecorder) { + ACSDK_DEBUG5(LX(__func__)); + if (!contextManager) { ACSDK_ERROR(LX("createFailed").d("reason", "nullContextManager")); } else { return std::shared_ptr( - new PostConnectSynchronizeStateSender(contextManager)); + new PostConnectSynchronizeStateSender(contextManager, metricRecorder)); } return nullptr; } PostConnectSynchronizeStateSender::PostConnectSynchronizeStateSender( - std::shared_ptr contextManager) : + std::shared_ptr contextManager, + std::shared_ptr metricRecorder) : m_contextManager{contextManager}, + m_metricRecorder{metricRecorder}, m_isStopping{false} { } @@ -150,6 +195,10 @@ bool PostConnectSynchronizeStateSender::performOperation(const std::shared_ptr const std::shared_ptr< acsdkPostConnectOperationProviderRegistrarInterfaces::PostConnectOperationProviderRegistrarInterface>& providerRegistrar, - const std::shared_ptr& contextManager) { + const std::shared_ptr& contextManager, + const std::shared_ptr& metricRecorder) { + ACSDK_DEBUG5(LX(__func__)); if (!providerRegistrar) { ACSDK_ERROR(LX("createFailed").d("reason", "nullProviderRegistrar")); return nullptr; @@ -47,7 +49,8 @@ std::shared_ptr ACSDK_ERROR(LX("createFailed").d("reason", "nullContextManager")); return nullptr; } - std::shared_ptr provider(new SynchronizeStateSenderFactory(contextManager)); + std::shared_ptr provider( + new SynchronizeStateSenderFactory(contextManager, metricRecorder)); if (!providerRegistrar->registerProvider(provider)) { return nullptr; } @@ -55,24 +58,28 @@ std::shared_ptr } std::shared_ptr SynchronizeStateSenderFactory::create( - std::shared_ptr contextManager) { + std::shared_ptr contextManager, + std::shared_ptr metricRecorder) { ACSDK_DEBUG5(LX(__func__)); if (!contextManager) { ACSDK_ERROR(LX("createFailed").d("reason", "nullContextManager")); } else { - return std::shared_ptr(new SynchronizeStateSenderFactory(contextManager)); + return std::shared_ptr( + new SynchronizeStateSenderFactory(contextManager, metricRecorder)); } return nullptr; } SynchronizeStateSenderFactory::SynchronizeStateSenderFactory( - std::shared_ptr contextManager) : - m_contextManager{contextManager} { + std::shared_ptr contextManager, + std::shared_ptr metricRecorder) : + m_contextManager{contextManager}, + m_metricRecorder{metricRecorder} { } std::shared_ptr SynchronizeStateSenderFactory::createPostConnectOperation() { ACSDK_DEBUG5(LX(__func__)); - return PostConnectSynchronizeStateSender::create(m_contextManager); + return PostConnectSynchronizeStateSender::create(m_contextManager, m_metricRecorder); } } // namespace synchronizeStateSender diff --git a/SynchronizeStateSender/test/CMakeLists.txt b/SynchronizeStateSender/test/CMakeLists.txt index 549775eae4..d9e72ac2af 100644 --- a/SynchronizeStateSender/test/CMakeLists.txt +++ b/SynchronizeStateSender/test/CMakeLists.txt @@ -1,5 +1,5 @@ set(INCLUDE_PATH "${SynchronizeStateSender_SOURCE_DIR}/include") -discover_unit_tests("${INCLUDE_PATH}" "SynchronizeStateSender;SDKInterfacesTests") +discover_unit_tests("${INCLUDE_PATH}" "SynchronizeStateSender;SDKInterfacesTests;UtilsCommonTestLib") add_definitions("-DACSDK_LOG_MODULE=synchronizeStateSenderTest") diff --git a/SynchronizeStateSender/test/PostConnectSynchronizeStateSenderTest.cpp b/SynchronizeStateSender/test/PostConnectSynchronizeStateSenderTest.cpp index 8bd99a2d07..4d5e37fd05 100644 --- a/SynchronizeStateSender/test/PostConnectSynchronizeStateSenderTest.cpp +++ b/SynchronizeStateSender/test/PostConnectSynchronizeStateSenderTest.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -153,6 +154,17 @@ TEST_F(PostConnectSynchronizeStateSenderTest, test_createWithNullContextManager) ASSERT_EQ(instance, nullptr); } +/** + * Test create with @c MetricRecorder. + */ +TEST_F(PostConnectSynchronizeStateSenderTest, test_createWithMetricRecorder) { + ASSERT_THAT(PostConnectSynchronizeStateSender::create(m_mockContextManager, nullptr), NotNull()); + ASSERT_THAT( + PostConnectSynchronizeStateSender::create( + m_mockContextManager, std::make_shared()), + NotNull()); +} + /** * Test GetPriority method for @c PostConnectSynchronizeStateSender. */ diff --git a/applications/CMakeLists.txt b/applications/CMakeLists.txt index ec8544b9c2..d97eb92cef 100644 --- a/applications/CMakeLists.txt +++ b/applications/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) add_subdirectory("acsdkApplicationAudioPipelineFactoryInterfaces") add_subdirectory("acsdkCBLAuthorizationDelegate") +add_subdirectory("acsdkDefaultDeviceSettingsManager") add_subdirectory("acsdkDefaultInternetConnectionMonitor") add_subdirectory("acsdkDefaultSampleApplicationOptions") add_subdirectory("acsdkLibcurlAlexaCommunications") add_subdirectory("acsdkLibcurlHTTPContentFetcher") add_subdirectory("acsdkNullMetricRecorder") +add_subdirectory("acsdkNullSystemTimeZone") add_subdirectory("acsdkPreviewAlexaClient") add_subdirectory("acsdkSampleApplicationCBLAuthRequester") add_subdirectory("acsdkSampleApplicationInterfaces") diff --git a/applications/acsdkDefaultDeviceSettingsManager/CMakeLists.txt b/applications/acsdkDefaultDeviceSettingsManager/CMakeLists.txt new file mode 100644 index 0000000000..9a64066d0c --- /dev/null +++ b/applications/acsdkDefaultDeviceSettingsManager/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.1 FATAL_ERROR) +project(acsdkDefaultDeviceSettingsManager LANGUAGES CXX) + +add_subdirectory("src") diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/DeviceSettingsManagerBuilder.h b/applications/acsdkDefaultDeviceSettingsManager/include/acsdkDeviceSettingsManager/DeviceSettingsManagerBuilder.h similarity index 79% rename from ApplicationUtilities/DefaultClient/include/DefaultClient/DeviceSettingsManagerBuilder.h rename to applications/acsdkDefaultDeviceSettingsManager/include/acsdkDeviceSettingsManager/DeviceSettingsManagerBuilder.h index d3445c793d..bf1daa660d 100644 --- a/ApplicationUtilities/DefaultClient/include/DefaultClient/DeviceSettingsManagerBuilder.h +++ b/applications/acsdkDefaultDeviceSettingsManager/include/acsdkDeviceSettingsManager/DeviceSettingsManagerBuilder.h @@ -13,8 +13,8 @@ * permissions and limitations under the License. */ -#ifndef ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_DEVICESETTINGSMANAGERBUILDER_H_ -#define ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_DEVICESETTINGSMANAGERBUILDER_H_ +#ifndef ACSDKDEVICESETTINGSMANAGER_DEVICESETTINGSMANAGERBUILDER_H_ +#define ACSDKDEVICESETTINGSMANAGER_DEVICESETTINGSMANAGERBUILDER_H_ #include #include @@ -38,13 +38,36 @@ #include namespace alexaClientSDK { -namespace defaultClient { +namespace acsdkDeviceSettingsManager { /** * The builder for @c DeviceSettingsManager. */ class DeviceSettingsManagerBuilder : public settings::SettingsManagerBuilderBase { public: + /** + * Factory method that creates a DeviceSettingsManager. + * + * @param settingStorage The storage used for settings. + * @param messageSender Sender used to send events related to this setting changes. + * @param connectionManager The ACL connection manager. + * @param dataManager A dataManager object that will track the CustomerDataHandler. + * @param localeAssetsManager The object that manages locale assets. + * @param doNotDisturbCapabilityAgent The DoNotDisturb CA. This parameter is needed because currently the + * DoNotDisturbSetting is managed differently than other settings in the SDK (ACSDK-2279). This is a legacy + * anti-pattern, and other CAs should be injected with the DeviceSettingsManager (as opposed to the + * DeviceSettingsManager being injected with the DND CA, as seen here). + * @param systemTimezone Optional parameter responsible for validating / applying timezone changes system wide. + */ + static std::shared_ptr createDeviceSettingsManager( + std::shared_ptr settingStorage, + std::shared_ptr messageSender, + std::shared_ptr connectionManager, + std::shared_ptr dataManager, + std::shared_ptr localeAssetsManager, + std::shared_ptr doNotDisturbCapabilityAgent, + std::shared_ptr systemTimezone = nullptr); + /** * Constructor. * @@ -197,7 +220,7 @@ std::shared_ptr> DeviceSettings return std::get(m_settingConfigs).setting; } -} // namespace defaultClient +} // namespace acsdkDeviceSettingsManager } // namespace alexaClientSDK -#endif // ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_DEVICESETTINGSMANAGERBUILDER_H_ +#endif // ACSDKDEVICESETTINGSMANAGER_DEVICESETTINGSMANAGERBUILDER_H_ diff --git a/applications/acsdkDefaultDeviceSettingsManager/include/acsdkDeviceSettingsManager/DeviceSettingsManagerComponent.h b/applications/acsdkDefaultDeviceSettingsManager/include/acsdkDeviceSettingsManager/DeviceSettingsManagerComponent.h new file mode 100644 index 0000000000..7473338b99 --- /dev/null +++ b/applications/acsdkDefaultDeviceSettingsManager/include/acsdkDeviceSettingsManager/DeviceSettingsManagerComponent.h @@ -0,0 +1,59 @@ +/* + * Copyright 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 ACSDKDEVICESETTINGSMANAGER_DEVICESETTINGSMANAGERCOMPONENT_H_ +#define ACSDKDEVICESETTINGSMANAGER_DEVICESETTINGSMANAGERCOMPONENT_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace alexaClientSDK { +namespace acsdkDeviceSettingsManager { + +/** + * Definition of a Manufactory Component for the default @c DeviceSettingsManagerBuilder. + */ +using DeviceSettingsManagerComponent = acsdkManufactory::Component< + std::shared_ptr, + acsdkManufactory::Import>, + acsdkManufactory::Import>, + acsdkManufactory::Import>, + acsdkManufactory::Import>, + acsdkManufactory::Import>, + acsdkManufactory::Import>, + acsdkManufactory::Import>>; + +/** + * Creates an manufactory component that exports @c DeviceSettingsManager. + * + * @return A component. + */ +DeviceSettingsManagerComponent getComponent(); + +} // namespace acsdkDeviceSettingsManager +} // namespace alexaClientSDK + +#endif // ACSDKDEVICESETTINGSMANAGER_DEVICESETTINGSMANAGERCOMPONENT_H_ diff --git a/applications/acsdkDefaultDeviceSettingsManager/src/CMakeLists.txt b/applications/acsdkDefaultDeviceSettingsManager/src/CMakeLists.txt new file mode 100644 index 0000000000..7bda7a1281 --- /dev/null +++ b/applications/acsdkDefaultDeviceSettingsManager/src/CMakeLists.txt @@ -0,0 +1,20 @@ +add_definitions("-DACSDK_LOG_MODULE=acsdkDefaultDeviceSettingsManager") +add_library(acsdkDefaultDeviceSettingsManager SHARED + DeviceSettingsManagerBuilder.cpp + DeviceSettingsManagerComponent.cpp) + +target_include_directories(acsdkDefaultDeviceSettingsManager PUBLIC + "${acsdkDefaultDeviceSettingsManager_SOURCE_DIR}/include") + +target_link_libraries(acsdkDefaultDeviceSettingsManager + AIP + AVSCommon + AVSSystem + acsdkAlerts + DeviceSettings + acsdkDoNotDisturb + acsdkManufactory + RegistrationManager) + +# install target +asdk_install() diff --git a/ApplicationUtilities/DefaultClient/src/DeviceSettingsManagerBuilder.cpp b/applications/acsdkDefaultDeviceSettingsManager/src/DeviceSettingsManagerBuilder.cpp similarity index 80% rename from ApplicationUtilities/DefaultClient/src/DeviceSettingsManagerBuilder.cpp rename to applications/acsdkDefaultDeviceSettingsManager/src/DeviceSettingsManagerBuilder.cpp index e8b25c6f6a..eb5d9b6dcc 100644 --- a/ApplicationUtilities/DefaultClient/src/DeviceSettingsManagerBuilder.cpp +++ b/applications/acsdkDefaultDeviceSettingsManager/src/DeviceSettingsManagerBuilder.cpp @@ -23,10 +23,10 @@ #include #include -#include "DefaultClient/DeviceSettingsManagerBuilder.h" +#include "acsdkDeviceSettingsManager/DeviceSettingsManagerBuilder.h" /// String to identify log entries originating from this file. -static const std::string TAG("SettingsManagerBuilder"); +static const std::string TAG("DeviceSettingsManagerBuilder"); /** * Create a LogEntry using this file's TAG and the specified event string. @@ -36,7 +36,7 @@ static const std::string TAG("SettingsManagerBuilder"); #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) namespace alexaClientSDK { -namespace defaultClient { +namespace acsdkDeviceSettingsManager { using namespace settings; @@ -64,6 +64,45 @@ static inline bool checkPointer(const std::shared_ptr& pointer, const return true; } +std::shared_ptr DeviceSettingsManagerBuilder::createDeviceSettingsManager( + std::shared_ptr settingStorage, + std::shared_ptr messageSender, + std::shared_ptr connectionManager, + std::shared_ptr dataManager, + std::shared_ptr localeAssetsManager, + std::shared_ptr doNotDisturbCapabilityAgent, + std::shared_ptr systemTimezone) { + if (!settingStorage || !messageSender || !connectionManager || !dataManager || !localeAssetsManager || + !doNotDisturbCapabilityAgent) { + ACSDK_ERROR(LX("createDeviceSettingsManagerBuilderFailed") + .d("isSettingStorageNull", !settingStorage) + .d("isMessageSenderNull", !messageSender) + .d("isConnectionManagerNull", !connectionManager) + .d("isDataManagerNull", !dataManager) + .d("isLocaleAssetsManagerNull", !localeAssetsManager) + .d("isDoNotDisturbCapabilityAgentNull", !doNotDisturbCapabilityAgent)); + return nullptr; + } + + DeviceSettingsManagerBuilder settingsManagerBuilder{settingStorage, messageSender, connectionManager, dataManager}; + settingsManagerBuilder.withDoNotDisturbSetting(doNotDisturbCapabilityAgent) + .withAlarmVolumeRampSetting() + .withWakeWordConfirmationSetting() + .withSpeechConfirmationSetting() + .withTimeZoneSetting(systemTimezone) + .withNetworkInfoSetting(); + + if (localeAssetsManager->getDefaultSupportedWakeWords().empty()) { + settingsManagerBuilder.withLocaleSetting(localeAssetsManager); + } else { + settingsManagerBuilder.withLocaleAndWakeWordsSettings(localeAssetsManager); + } + + auto deviceSettingsManager = settingsManagerBuilder.build(); + + return std::move(deviceSettingsManager); +} + DeviceSettingsManagerBuilder::DeviceSettingsManagerBuilder( std::shared_ptr settingStorage, std::shared_ptr messageSender, @@ -97,7 +136,7 @@ std::unique_ptr DeviceSettingsManagerBuilder::build() { return nullptr; } - std::unique_ptr manager{new DeviceSettingsManager(m_dataManager)}; + std::unique_ptr manager{new DeviceSettingsManager(m_dataManager, m_settingConfigs)}; if (!addSetting(*this, *manager)) { ACSDK_ERROR(LX("buildFailed").d("reason", "addSettingFailed")); return nullptr; @@ -222,5 +261,5 @@ DeviceSettingsManagerBuilder& DeviceSettingsManagerBuilder::withSynchronizedSett return *this; } -} // namespace defaultClient +} // namespace acsdkDeviceSettingsManager } // namespace alexaClientSDK diff --git a/applications/acsdkDefaultDeviceSettingsManager/src/DeviceSettingsManagerComponent.cpp b/applications/acsdkDefaultDeviceSettingsManager/src/DeviceSettingsManagerComponent.cpp new file mode 100644 index 0000000000..02fdd8f236 --- /dev/null +++ b/applications/acsdkDefaultDeviceSettingsManager/src/DeviceSettingsManagerComponent.cpp @@ -0,0 +1,32 @@ +/* + * Copyright 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 + +#include + +#include "acsdkDeviceSettingsManager/DeviceSettingsManagerBuilder.h" +#include "acsdkDeviceSettingsManager/DeviceSettingsManagerComponent.h" + +namespace alexaClientSDK { +namespace acsdkDeviceSettingsManager { + +DeviceSettingsManagerComponent getComponent() { + return acsdkManufactory::ComponentAccumulator<>().addRetainedFactory( + DeviceSettingsManagerBuilder::createDeviceSettingsManager); +} + +} // namespace acsdkDeviceSettingsManager +} // namespace alexaClientSDK diff --git a/applications/acsdkLibcurlAlexaCommunications/include/acsdkAlexaCommunications/AlexaCommunicationsComponent.h b/applications/acsdkLibcurlAlexaCommunications/include/acsdkAlexaCommunications/AlexaCommunicationsComponent.h index 36abd9c6bd..6b8a087449 100644 --- a/applications/acsdkLibcurlAlexaCommunications/include/acsdkAlexaCommunications/AlexaCommunicationsComponent.h +++ b/applications/acsdkLibcurlAlexaCommunications/include/acsdkAlexaCommunications/AlexaCommunicationsComponent.h @@ -18,12 +18,14 @@ #include +#include #include #include #include #include #include #include +#include #include namespace alexaClientSDK { @@ -38,6 +40,9 @@ using AlexaCommunicationsComponent = acsdkManufactory::Component< acsdkManufactory::Import>, acsdkManufactory::Import>, acsdkManufactory::Import>, + acsdkManufactory::Import>, acsdkManufactory::Import>, acsdkManufactory::Import>>; diff --git a/applications/acsdkLibcurlAlexaCommunications/src/AlexaCommunicationsComponent.cpp b/applications/acsdkLibcurlAlexaCommunications/src/AlexaCommunicationsComponent.cpp index 3f44bbd212..eef5eb03f5 100644 --- a/applications/acsdkLibcurlAlexaCommunications/src/AlexaCommunicationsComponent.cpp +++ b/applications/acsdkLibcurlAlexaCommunications/src/AlexaCommunicationsComponent.cpp @@ -14,6 +14,7 @@ */ #include +#include #include #include @@ -31,8 +32,26 @@ namespace acsdkAlexaCommunications { using namespace acl; using namespace acsdkManufactory; using namespace avsCommon::sdkInterfaces; +using namespace avsCommon::utils::http2; using namespace avsCommon::utils::libcurlUtils; +/** + * This function adapts an Annotated + * to a shared_ptr and creates a HTTP2ConnectionFactory. + * + * TODO: ACSDK-4957 + * @note This is only necessary because LibcurlHTTP2ConnectionFactory is in AVSCommon and is prohibited + * from using acsdkManufactory::Annotated<> because acsdkManufactory depends upon AVSCommon. This will + * be fixed when acsdkManufactory's depenency upon AVSCommon is removed. + * + * @param callbackFactory The Annotated curl options callback factory + * @return A HTTP2ConnectionFactoryInterface. + */ +static std::shared_ptr createHTTP2ConnectionFactory( + const Annotated& callbackFactory) { + return LibcurlHTTP2ConnectionFactory::createHTTP2ConnectionFactoryInterface(callbackFactory); +} + AlexaCommunicationsComponent getComponent() { return ComponentAccumulator<>() .addComponent(acsdkCore::getComponent()) @@ -40,7 +59,7 @@ AlexaCommunicationsComponent getComponent() { .addRetainedFactory(AVSConnectionManager::createAVSConnectionManagerInterface) .addRetainedFactory(AVSConnectionManager::createMessageSenderInterface) .addRetainedFactory(HTTP2TransportFactory::createTransportFactoryInterface) - .addRetainedFactory(LibcurlHTTP2ConnectionFactory::createHTTP2ConnectionFactoryInterface) + .addRetainedFactory(createHTTP2ConnectionFactory) .addRetainedFactory(MessageRouter::createMessageRouterInterface) .addRetainedFactory(PostConnectSequencerFactory::createPostConnectFactoryInterface); } diff --git a/applications/acsdkLibcurlHTTPContentFetcher/include/acsdkHTTPContentFetcher/HTTPContentFetcherComponent.h b/applications/acsdkLibcurlHTTPContentFetcher/include/acsdkHTTPContentFetcher/HTTPContentFetcherComponent.h index 5dcd1df1f8..517cca89c2 100644 --- a/applications/acsdkLibcurlHTTPContentFetcher/include/acsdkHTTPContentFetcher/HTTPContentFetcherComponent.h +++ b/applications/acsdkLibcurlHTTPContentFetcher/include/acsdkHTTPContentFetcher/HTTPContentFetcherComponent.h @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -28,8 +29,11 @@ namespace acsdkHTTPContentFetcher { /** * Manufactory Component definition for Libcurl implementation of HTTPContentFetcherInbterfaceFactoryInterface */ -using HTTPContentFetcherComponent = - acsdkManufactory::Component>; +using HTTPContentFetcherComponent = acsdkManufactory::Component< + std::shared_ptr, + acsdkManufactory::Import>>; /** * Get the default @c Manufactory component for creating instances of HTTPContentFetcherInterfaceFactoryInterface. diff --git a/applications/acsdkLibcurlHTTPContentFetcher/src/HTTPContentFetcherComponent.cpp b/applications/acsdkLibcurlHTTPContentFetcher/src/HTTPContentFetcherComponent.cpp index 1351db7cae..80cb45b6e9 100644 --- a/applications/acsdkLibcurlHTTPContentFetcher/src/HTTPContentFetcherComponent.cpp +++ b/applications/acsdkLibcurlHTTPContentFetcher/src/HTTPContentFetcherComponent.cpp @@ -23,11 +23,30 @@ namespace alexaClientSDK { namespace acsdkHTTPContentFetcher { using namespace acsdkManufactory; +using namespace avsCommon::sdkInterfaces; using namespace avsCommon::utils::libcurlUtils; +/** + * This function adapts an Annotated to a shared_ptr + * and returns a HTTPContentFetcherInterfaceFactoryInterface. + * + * TODO: ACSDK-4957 + * @note This is only necessary because HTTPContentFetcherFactory is in AVSCommon and is prohibited + * from using acsdkManufactory::Annotated<> because acsdkManufactory depends upon AVSCommon. This will + * be fixed when acsdkManufactory's depenency upon AVSCommon is removed. + * + * @param callbackFactory The Annotated<> curl options callback factory. + * @return A HTTPContentFetcherInterfaceFactoryInterface. + */ +static std::shared_ptr createHTTPContentFetcherFactory( + const Annotated& + callbackFactory) { + return HTTPContentFetcherFactory::createHTTPContentFetcherInterfaceFactoryInterface(callbackFactory); +} + HTTPContentFetcherComponent getComponent() { - return ComponentAccumulator<>().addRetainedFactory( - HTTPContentFetcherFactory::createHTTPContentFetcherInterfaceFactoryInterface); + return ComponentAccumulator<>().addRetainedFactory(createHTTPContentFetcherFactory); } } // namespace acsdkHTTPContentFetcher diff --git a/applications/acsdkNullSystemTimeZone/CMakeLists.txt b/applications/acsdkNullSystemTimeZone/CMakeLists.txt new file mode 100644 index 0000000000..72b73847ec --- /dev/null +++ b/applications/acsdkNullSystemTimeZone/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.1 FATAL_ERROR) +project(acsdkNullSystemTimeZone LANGUAGES CXX) + +add_subdirectory("src") diff --git a/applications/acsdkNullSystemTimeZone/include/acsdkSystemTimeZone/SystemTimeZoneComponent.h b/applications/acsdkNullSystemTimeZone/include/acsdkSystemTimeZone/SystemTimeZoneComponent.h new file mode 100644 index 0000000000..3963efa5b4 --- /dev/null +++ b/applications/acsdkNullSystemTimeZone/include/acsdkSystemTimeZone/SystemTimeZoneComponent.h @@ -0,0 +1,43 @@ +/* + * Copyright 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 ACSDKSYSTEMTIMEZONE_SYSTEMTIMEZONECOMPONENT_H_ +#define ACSDKSYSTEMTIMEZONE_SYSTEMTIMEZONECOMPONENT_H_ + +#include + +#include +#include + +namespace alexaClientSDK { +namespace acsdkSystemTimeZone { + +/** + * Manufactory Component definition for null implementation of SystemTimeZoneInterface + */ +using SystemTimeZoneComponent = + acsdkManufactory::Component>; + +/** + * Get the Manufactory component for creating null instances of SystemTimeZoneInterface. + * + * @return The default @c Manufactory component for creating null instances of SystemTimeZoneInterface. + */ +SystemTimeZoneComponent getComponent(); + +} // namespace acsdkSystemTimeZone +} // namespace alexaClientSDK + +#endif // ACSDKSYSTEMTIMEZONE_SYSTEMTIMEZONECOMPONENT_H_ diff --git a/applications/acsdkNullSystemTimeZone/src/CMakeLists.txt b/applications/acsdkNullSystemTimeZone/src/CMakeLists.txt new file mode 100644 index 0000000000..ffc04bc91c --- /dev/null +++ b/applications/acsdkNullSystemTimeZone/src/CMakeLists.txt @@ -0,0 +1,12 @@ +add_definitions("-DACSDK_LOG_MODULE=acsdkNullSystemTimeZone") +add_library(acsdkNullSystemTimeZone SHARED + SystemTimeZoneComponent.cpp) + +target_include_directories(acsdkNullSystemTimeZone PUBLIC + "${acsdkNullSystemTimeZone_SOURCE_DIR}/include") + +target_link_libraries(acsdkNullSystemTimeZone + acsdkManufactory) + +# install target +asdk_install() diff --git a/applications/acsdkNullSystemTimeZone/src/SystemTimeZoneComponent.cpp b/applications/acsdkNullSystemTimeZone/src/SystemTimeZoneComponent.cpp new file mode 100644 index 0000000000..81cf71c00b --- /dev/null +++ b/applications/acsdkNullSystemTimeZone/src/SystemTimeZoneComponent.cpp @@ -0,0 +1,31 @@ +/* + * Copyright 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 + +#include "acsdkSystemTimeZone/SystemTimeZoneComponent.h" + +namespace alexaClientSDK { +namespace acsdkSystemTimeZone { + +using namespace acsdkManufactory; + +SystemTimeZoneComponent getComponent() { + return ComponentAccumulator<>().addInstance>( + nullptr); +} + +} // namespace acsdkSystemTimeZone +} // namespace alexaClientSDK diff --git a/applications/acsdkPreviewAlexaClient/include/acsdkPreviewAlexaClient/PreviewAlexaClient.h b/applications/acsdkPreviewAlexaClient/include/acsdkPreviewAlexaClient/PreviewAlexaClient.h index c90bc342b6..258046a007 100644 --- a/applications/acsdkPreviewAlexaClient/include/acsdkPreviewAlexaClient/PreviewAlexaClient.h +++ b/applications/acsdkPreviewAlexaClient/include/acsdkPreviewAlexaClient/PreviewAlexaClient.h @@ -172,9 +172,6 @@ class PreviewAlexaClient { /// The vector of components requiring shutdown std::vector> m_shutdownRequiredList; - /// The @c MediaPlayer used by @c Alerts. - std::shared_ptr m_alertsMediaPlayer; - /// The @c MediaPlayer used by @c Bluetooth. std::shared_ptr m_bluetoothMediaPlayer; diff --git a/applications/acsdkPreviewAlexaClient/include/acsdkPreviewAlexaClient/PreviewAlexaClientComponent.h b/applications/acsdkPreviewAlexaClient/include/acsdkPreviewAlexaClient/PreviewAlexaClientComponent.h index 39b08376de..896d482a31 100644 --- a/applications/acsdkPreviewAlexaClient/include/acsdkPreviewAlexaClient/PreviewAlexaClientComponent.h +++ b/applications/acsdkPreviewAlexaClient/include/acsdkPreviewAlexaClient/PreviewAlexaClientComponent.h @@ -64,6 +64,7 @@ namespace acsdkPreviewAlexaClient { * remain stable. */ using PreviewAlexaClientComponent = acsdkManufactory::Component< + std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, @@ -92,6 +93,8 @@ using PreviewAlexaClientComponent = acsdkManufactory::Component< std::shared_ptr, std::shared_ptr, std::shared_ptr, + std::shared_ptr, + std::shared_ptr, acsdkManufactory::Annotated< avsCommon::sdkInterfaces::endpoints::DefaultEndpointAnnotation, avsCommon::sdkInterfaces::endpoints::EndpointBuilderInterface>, @@ -99,11 +102,15 @@ using PreviewAlexaClientComponent = acsdkManufactory::Component< std::shared_ptr, std::shared_ptr, std::shared_ptr, + std::shared_ptr, std::shared_ptr, + std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, - std::shared_ptr>; + std::shared_ptr, + std::shared_ptr, + std::shared_ptr>; /** * Get the manufactory @c Component for PreviewAlexaClient. diff --git a/applications/acsdkPreviewAlexaClient/src/CMakeLists.txt b/applications/acsdkPreviewAlexaClient/src/CMakeLists.txt index 21a3286dee..3ff9571304 100644 --- a/applications/acsdkPreviewAlexaClient/src/CMakeLists.txt +++ b/applications/acsdkPreviewAlexaClient/src/CMakeLists.txt @@ -11,11 +11,15 @@ target_include_directories(LibPreviewAlexaClient PUBLIC target_link_libraries(LibPreviewAlexaClient ${ACSDKALEXACOMMUNICATIONS_LIB} ${ACSDKAUTHORIZATIONDELEGATE_LIB} + ${ACSDKDEVICESETTINGSMANAGER_LIB} ${ACSDKHTTPCONTENTFETCHER_LIB} ${ACSDKINTERNETCONNECTIONMONITOR_LIB} ${ACSDKMETRICRECORDER_LIB} + ${ACSDKSYSTEMTIMEZONE_LIB} + acsdkAlerts acsdkApplicationAudioPipelineFactoryInterfaces acsdkCore + acsdkLibcurlHTTPContentFetcher acsdkManufactory acsdkSampleApplicationInterfaces LibSampleApp) @@ -40,4 +44,4 @@ add_executable(PreviewAlexaClient target_link_libraries(PreviewAlexaClient LibPreviewAlexaClient) # install target -asdk_install_targets(LibPreviewAlexaClient TRUE) \ No newline at end of file +asdk_install_targets(LibPreviewAlexaClient TRUE) diff --git a/applications/acsdkPreviewAlexaClient/src/PreviewAlexaClient.cpp b/applications/acsdkPreviewAlexaClient/src/PreviewAlexaClient.cpp index e4db4124bc..02d4e8875f 100644 --- a/applications/acsdkPreviewAlexaClient/src/PreviewAlexaClient.cpp +++ b/applications/acsdkPreviewAlexaClient/src/PreviewAlexaClient.cpp @@ -25,7 +25,6 @@ #include #include #include