From f2dab7ee26e59caa41fa861e04df256fa8563ef1 Mon Sep 17 00:00:00 2001 From: Caleigh Minshall Date: Mon, 29 Mar 2021 12:59:51 -0700 Subject: [PATCH] Version 1.23.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/CMakeLists.txt | 2 +- ACL/include/ACL/AVSConnectionManager.h | 5 + .../ACL/Transport/MessageRequestHandler.h | 4 + ACL/src/AVSConnectionManager.cpp | 82 +- ACL/src/Transport/MessageRequestHandler.cpp | 99 +- ACL/src/Transport/MessageRouter.cpp | 7 + ACL/test/Transport/HTTP2TransportTest.cpp | 10 +- ADSL/CMakeLists.txt | 2 +- AFML/CMakeLists.txt | 2 +- .../AVSCommon/AVS/CapabilityChangeNotifier.h | 36 + .../AVS/CapabilityChangeNotifierInterface.h | 36 + .../AVSCommon/AVS/DialogUXStateAggregator.h | 7 +- .../AVSCommon/AVS/EditableMessageRequest.h | 61 ++ .../include/AVSCommon/AVS/MessageRequest.h | 53 +- AVSCommon/AVS/src/AVSDirective.cpp | 4 + .../AVS/src/Attachment/AttachmentManager.cpp | 1 + AVSCommon/AVS/src/CapabilityResources.cpp | 11 +- AVSCommon/AVS/src/DialogUXStateAggregator.cpp | 68 +- AVSCommon/AVS/src/EditableMessageRequest.cpp | 60 ++ AVSCommon/AVS/src/MessageRequest.cpp | 47 +- AVSCommon/AVS/test/AVSDirectiveTest.cpp | 25 + .../AVS/test/CapabilityResourcesTest.cpp | 14 +- .../AVS/test/DialogUXStateAggregatorTest.cpp | 54 +- .../AVS/test/EditableMessageRequestTest.cpp | 101 ++ AVSCommon/AVS/test/MessageRequestTest.cpp | 80 ++ AVSCommon/CMakeLists.txt | 2 +- .../SDKInterfaces/CallManagerInterface.h | 18 + .../CallStateObserverInterface.h | 41 +- .../CapabilitiesDelegateInterface.h | 10 +- .../CapabilitiesDelegateObserverInterface.h | 136 +++ .../CapabilitiesObserverInterface.h | 109 +- ...lityConfigurationChangeObserverInterface.h | 49 + .../SDKInterfaces/ChannelVolumeInterface.h | 4 +- .../LocaleAssetsManagerInterface.h | 28 +- .../LocaleAssetsObserverInterface.h | 43 + .../MessageRequestObserverInterface.h | 9 + .../SpeakerManagerObserverInterface.h | 7 +- .../SDKInterfaces/MockCapabilitiesDelegate.h | 4 +- .../SDKInterfaces/MockLocaleAssetsManager.h | 6 + .../Utils/Bluetooth/BluetoothEventBus.h | 6 + .../Utils/HTTP2/HTTP2ResponseFinishedStatus.h | 2 +- .../include/AVSCommon/Utils/Logger/LogEntry.h | 14 +- .../Utils/MediaPlayer/MediaPlayerInterface.h | 22 + .../include/AVSCommon/Utils/SDKVersion.h | 4 +- .../AVSCommon/Utils/SDS/SharedDataStream.h | 9 +- .../AVSCommon/Utils/Timing/MultiTimer.h | 7 + .../Utils/Timing/SystemClockMonitor.h | 77 -- AVSCommon/Utils/src/BluetoothEventBus.cpp | 4 + .../src/HTTP2/HTTP2MimeResponseDecoder.cpp | 45 +- AVSCommon/Utils/src/JSON/JSONUtils.cpp | 4 +- .../src/LibcurlUtils/LibcurlHTTP2Request.cpp | 19 +- AVSCommon/Utils/src/MultiTimer.cpp | 4 + AVSCommon/Utils/src/SystemClockMonitor.cpp | 77 -- .../Threading/ConditionVariableWrapper.cpp | 38 +- .../Utils/MediaPlayer/MockMediaPlayer.h | 5 + .../Utils/test/Common/MockMediaPlayer.cpp | 8 + AVSCommon/Utils/test/LoggerTest.cpp | 2 +- AVSCommon/Utils/test/MIMEParserTest.cpp | 298 +++++- AVSCommon/Utils/test/SharedDataStreamTest.cpp | 21 + AVSGatewayManager/CMakeLists.txt | 2 +- .../AndroidUtilities/CMakeLists.txt | 2 +- ApplicationUtilities/CMakeLists.txt | 2 +- .../DefaultClient/CMakeLists.txt | 2 +- .../include/DefaultClient/DefaultClient.h | 149 ++- .../DefaultClient/DefaultClientComponent.h | 22 +- .../ExternalCapabilitiesBuilderInterface.h | 5 +- .../DefaultClient/src/CMakeLists.txt | 2 + .../DefaultClient/src/DefaultClient.cpp | 191 ++-- .../src/DefaultClientComponent.cpp | 28 +- .../Audio/src/AlertsAudioFactory.cpp | 9 +- .../SystemSoundPlayer/SystemSoundPlayer.h | 19 + .../src/SystemSoundPlayer.cpp | 40 + .../SystemSoundPlayer/test/CMakeLists.txt | 2 +- .../test/SystemSoundPlayerTest.cpp | 109 +- BluetoothImplementations/BlueZ/CMakeLists.txt | 2 - .../BlueZ/BlueZBluetoothDeviceManager.h | 12 +- .../BlueZ/PulseAudioBluetoothInitializer.h | 2 +- .../BlueZ/src/BlueZBluetoothDeviceManager.cpp | 6 + .../BlueZ/src/DBusConnection.cpp | 1 - .../src/PulseAudioBluetoothInitializer.cpp | 4 +- .../BlueZ/test/MPRISPlayerTest.cpp | 31 +- BluetoothImplementations/CMakeLists.txt | 2 +- CHANGELOG.md | 3 + CMakeLists.txt | 13 +- CapabilitiesDelegate/CMakeLists.txt | 2 +- .../CapabilitiesDelegate.h | 16 +- .../src/CapabilitiesDelegate.cpp | 47 +- .../test/CapabilitiesDelegateTest.cpp | 266 ++--- ...r.h => MockCapabilitiesDelegateObserver.h} | 17 +- CapabilityAgents/AIP/CMakeLists.txt | 2 +- .../AIP/include/AIP/AudioInputProcessor.h | 190 +++- .../AIP/src/AudioInputProcessor.cpp | 410 ++++++-- CapabilityAgents/AIP/src/CMakeLists.txt | 3 +- .../AIP/test/AudioInputProcessorTest.cpp | 936 +++++++++++++++--- CapabilityAgents/Alexa/CMakeLists.txt | 2 +- .../Alexa/AlexaEventProcessedNotifier.h | 17 +- .../Alexa/src/AlexaEventProcessedNotifier.cpp | 29 + CapabilityAgents/Alexa/src/CMakeLists.txt | 1 + CapabilityAgents/ApiGateway/CMakeLists.txt | 2 +- CapabilityAgents/CMakeLists.txt | 2 +- .../InteractionModel/CMakeLists.txt | 2 +- .../SpeakerManager/CMakeLists.txt | 2 +- .../include/SpeakerManager/SpeakerManager.h | 41 +- .../SpeakerManager/src/SpeakerManager.cpp | 225 +++-- .../test/SpeakerManagerTest.cpp | 189 ++-- .../SpeechSynthesizer/CMakeLists.txt | 2 +- .../include/System/SystemCapabilityProvider.h | 38 +- CapabilityAgents/System/src/CMakeLists.txt | 9 +- .../System/src/SystemCapabilityProvider.cpp | 44 +- .../test/SystemCapabilityProviderTest.cpp | 171 ++++ Captions/CMakeLists.txt | 2 +- CertifiedSender/CMakeLists.txt | 2 +- .../CertifiedSender/SQLiteMessageStorage.h | 34 + CertifiedSender/src/CertifiedSender.cpp | 19 + CertifiedSender/src/SQLiteMessageStorage.cpp | 138 ++- CertifiedSender/test/CMakeLists.txt | 2 +- CertifiedSender/test/CertifiedSenderTest.cpp | 104 +- CertifiedSender/test/MessageStorageTest.cpp | 232 ++++- ContextManager/CMakeLists.txt | 2 +- ContextManager/src/ContextManager.cpp | 6 +- Diagnostics/CMakeLists.txt | 2 +- Endpoints/CMakeLists.txt | 2 +- .../Endpoints/EndpointRegistrationManager.h | 6 +- Endpoints/src/EndpointBuilder.cpp | 17 +- Endpoints/src/EndpointRegistrationManager.cpp | 10 +- .../test/EndpointRegistrationManagerTest.cpp | 84 +- Integration/AlexaClientSDKConfig.json | 224 +---- Integration/CMakeLists.txt | 2 +- .../include/Integration/TestMediaPlayer.h | 7 + .../src/TestExceptionEncounteredSender.cpp | 4 +- Integration/src/TestMediaPlayer.cpp | 9 + Integration/test/AlertsIntegrationTest.cpp | 2 + .../AudioInputProcessorIntegrationTest.cpp | 2 + .../test/AudioPlayerIntegrationTest.cpp | 2 + InterruptModel/CMakeLists.txt | 2 +- KWD/KWDProvider/CMakeLists.txt | 2 +- KWD/KittAi/CMakeLists.txt | 2 +- KWD/Sensory/CMakeLists.txt | 2 +- .../AndroidSLESMediaPlayer/CMakeLists.txt | 2 +- .../AndroidSLESMediaPlayer.h | 9 + .../AndroidSLESSpeaker.h | 2 - .../src/AndroidSLESMediaPlayer.cpp | 12 +- .../test/AndroidSLESMediaPlayerTest.cpp | 10 +- .../GStreamerMediaPlayer/CMakeLists.txt | 2 +- .../include/MediaPlayer/MediaPlayer.h | 10 + .../GStreamerMediaPlayer/src/MediaPlayer.cpp | 12 +- Metrics/MetricRecorder/CMakeLists.txt | 2 +- Metrics/SampleMetricSink/CMakeLists.txt | 2 +- Metrics/UplCalculator/CMakeLists.txt | 2 +- PlaylistParser/CMakeLists.txt | 2 +- PlaylistParser/src/ContentDecrypter.cpp | 10 +- .../src/UrlContentToAttachmentConverter.cpp | 4 +- RegistrationManager/CMakeLists.txt | 2 +- .../RegistrationManager/CustomerDataManager.h | 7 + .../src/CustomerDataManager.cpp | 4 + .../CBLAuthDelegate/CMakeLists.txt | 2 +- .../CBLAuthDelegateStorageInterface.h | 3 + .../SQLiteCBLAuthDelegateStorage.h | 3 + .../CBLAuthDelegate/src/CBLAuthDelegate.cpp | 4 +- .../CBLAuthDelegate/src/CMakeLists.txt | 3 + SampleApp/Authorization/CMakeLists.txt | 2 +- SampleApp/CMakeLists.txt | 2 +- .../SampleApp/ExternalCapabilitiesBuilder.h | 10 +- .../include/SampleApp/InteractionManager.h | 20 + .../include/SampleApp/LocaleAssetsManager.h | 68 +- .../SampleApp/SampleApplicationComponent.h | 10 +- SampleApp/include/SampleApp/UIManager.h | 10 +- .../include/SampleApp/UserInputManager.h | 8 +- SampleApp/src/ExternalCapabilitiesBuilder.cpp | 12 +- SampleApp/src/InteractionManager.cpp | 40 + SampleApp/src/LocaleAssetsManager.cpp | 111 ++- SampleApp/src/SampleApplication.cpp | 3 +- SampleApp/src/SampleApplicationComponent.cpp | 40 +- SampleApp/src/UIManager.cpp | 8 +- SampleApp/src/UserInputManager.cpp | 6 +- Settings/CMakeLists.txt | 2 +- .../OpusEncoderContext/CMakeLists.txt | 2 +- .../SpeechEncoder/OpusEncoderContext.h | 2 +- .../src/OpusEncoderContext.cpp | 23 +- SpeechEncoder/src/SpeechEncoder.cpp | 1 + SpeechEncoder/test/SpeechEncoderTest.cpp | 60 +- Storage/CMakeLists.txt | 2 +- Storage/SQLiteStorage/CMakeLists.txt | 2 +- Storage/SQLiteStorage/src/SQLiteUtils.cpp | 1 + SynchronizeStateSender/CMakeLists.txt | 2 +- ThirdParty/MultipartParser/CMakeLists.txt | 5 + applications/CMakeLists.txt | 10 +- .../AndroidApplicationAudioPipelineFactory.h | 54 +- ...ApplicationAudioPipelineFactoryComponent.h | 58 ++ ...AndroidApplicationAudioPipelineFactory.cpp | 20 +- ...plicationAudioPipelineFactoryComponent.cpp | 30 + .../src/CMakeLists.txt | 4 +- .../CMakeLists.txt | 4 + .../BluetoothImplementationComponent.h | 45 + .../src/BluetoothImplementationComponent.cpp | 48 + .../src/CMakeLists.txt | 15 + ...ApplicationAudioPipelineFactoryComponent.h | 56 ++ .../CustomApplicationAudioPipelineFactory.h | 41 +- ...plicationAudioPipelineFactoryComponent.cpp | 30 + .../src/CMakeLists.txt | 4 +- .../CustomApplicationAudioPipelineFactory.cpp | 8 +- ...ApplicationAudioPipelineFactoryComponent.h | 57 ++ ...GstreamerApplicationAudioPipelineFactory.h | 45 +- ...plicationAudioPipelineFactoryComponent.cpp | 30 + .../src/CMakeLists.txt | 2 + ...treamerApplicationAudioPipelineFactory.cpp | 8 +- .../CMakeLists.txt | 4 + .../BluetoothImplementationComponent.h | 45 + .../src/BluetoothImplementationComponent.cpp | 30 + .../src/CMakeLists.txt | 14 + .../acsdkPreviewAlexaClient/CMakeLists.txt | 2 +- .../PreviewAlexaClient.h | 15 - .../PreviewAlexaClientComponent.h | 11 +- .../src/CMakeLists.txt | 10 +- .../src/PreviewAlexaClient.cpp | 119 +-- .../src/PreviewAlexaClientComponent.cpp | 26 +- build/BuildDefaults.cmake | 130 +-- build/cmake/Bluetooth.cmake | 28 - build/cmake/DefaultLibNames.cmake | 20 - .../Alerts/acsdkAlerts/CMakeLists.txt | 2 +- .../acsdkAlerts/AlertsCapabilityAgent.h | 58 +- .../include/acsdkAlerts/AlertsComponent.h | 11 +- .../include/acsdkAlerts/Renderer/Renderer.h | 5 +- .../acsdkAlerts/src/AlertsCapabilityAgent.cpp | 18 +- .../acsdkAlerts/src/AlertsComponent.cpp | 90 +- .../Alerts/acsdkAlerts/src/CMakeLists.txt | 1 + .../acsdkAlerts/src/Renderer/Renderer.cpp | 10 +- .../src/Storage/SQLiteAlertStorage.cpp | 27 +- .../Alerts/acsdkAlerts/test/CMakeLists.txt | 2 + .../test/Renderer/RendererTest.cpp | 9 +- .../acsdkAlertsInterfaces/CMakeLists.txt | 2 +- .../AlertsCapabilityAgentInterface.h | 65 ++ .../acsdkAudioPlayer/CMakeLists.txt | 2 +- .../include/acsdkAudioPlayer/AudioPlayer.h | 16 + .../acsdkAudioPlayer/src/AudioPlayer.cpp | 172 +++- .../acsdkAudioPlayer/src/CMakeLists.txt | 3 + .../acsdkAudioPlayerInterfaces/CMakeLists.txt | 2 +- .../Bluetooth/acsdkBluetooth/CMakeLists.txt | 2 +- .../BasicDeviceConnectionRulesProvider.h | 62 ++ .../include/acsdkBluetooth/Bluetooth.h | 92 +- .../acsdkBluetooth/BluetoothComponent.h | 77 ++ .../acsdkBluetooth/BluetoothNotifier.h | 43 + .../DeviceConnectionRulesAdapter.h | 61 ++ .../acsdkBluetooth/SQLiteBluetoothStorage.h | 19 +- .../BasicDeviceConnectionRulesProvider.cpp | 48 + .../acsdkBluetooth/src/Bluetooth.cpp | 184 ++-- .../acsdkBluetooth/src/BluetoothComponent.cpp | 39 + .../acsdkBluetooth/src/BluetoothNotifier.cpp | 29 + .../acsdkBluetooth/src/CMakeLists.txt | 7 + .../src/DeviceConnectionRulesAdapter.cpp | 33 + .../src/SQLiteBluetoothStorage.cpp | 16 +- .../acsdkBluetooth/test/BluetoothTest.cpp | 462 +++++---- .../acsdkBluetooth/test/CMakeLists.txt | 2 +- .../acsdkBluetoothInterfaces/CMakeLists.txt | 4 +- ...thDeviceConnectionRulesProviderInterface.h | 49 + .../BluetoothNotifierInterface.h | 36 + .../BluetoothStorageInterface.h | 14 +- .../test/CMakeLists.txt | 2 +- .../acsdkDoNotDisturb/CMakeLists.txt | 2 +- .../Equalizer/acsdkEqualizer/CMakeLists.txt | 2 +- .../CMakeLists.txt | 2 +- .../acsdkEqualizerInterfaces/CMakeLists.txt | 2 +- .../test/CMakeLists.txt | 2 +- .../acsdkExternalMediaPlayer/CMakeLists.txt | 2 +- .../ExternalMediaAdapterHandler.h | 6 +- .../StaticExternalMediaPlayerAdapterHandler.h | 3 +- .../src/ExternalMediaAdapterHandler.cpp | 6 +- .../src/ExternalMediaPlayer.cpp | 28 +- ...taticExternalMediaPlayerAdapterHandler.cpp | 13 +- .../test/ExternalMediaAdapterHandlerTest.cpp | 19 +- .../test/ExternalMediaPlayerTest.cpp | 16 +- .../AdapterUtils.h | 27 + .../ExternalMediaAdapterConstants.h | 12 + .../ExternalMediaAdapterHandlerInterface.h | 3 +- .../ExternalMediaAdapterInterface.h | 91 +- .../src/AdapterUtils.cpp | 139 ++- .../src/CMakeLists.txt | 2 +- .../acsdkMultiRoomMusic/CMakeLists.txt | 4 +- .../acsdkMultiRoomMusic/MRMCapabilityAgent.h | 9 +- .../acsdkMultiRoomMusic/MRMHandlerInterface.h | 13 +- .../src/MRMCapabilityAgent.cpp | 8 +- .../acsdkNotifications/CMakeLists.txt | 2 +- .../CMakeLists.txt | 2 +- cmakeBuild/BuildDefaults.cmake | 131 +++ {build => cmakeBuild}/cmake/A4B.cmake | 0 {build => cmakeBuild}/cmake/ACSUtils.cmake | 0 {build => cmakeBuild}/cmake/Android.cmake | 0 cmakeBuild/cmake/Bluetooth.cmake | 40 + .../cmake/BuildOptions.cmake | 11 +- {build => cmakeBuild}/cmake/Captions.cmake | 0 {build => cmakeBuild}/cmake/Ccache.cmake | 0 .../CodeCoverage/CTestCustom.cmake.template | 0 .../cmake/CodeCoverage/CodeCoverage.cmake | 0 .../cmake/CodeCoverage/postCTest.sh | 0 .../cmake/CodeCoverage/preCTest.sh | 0 {build => cmakeBuild}/cmake/Comms.cmake | 0 {build => cmakeBuild}/cmake/Crypto.cmake | 0 {build => cmakeBuild}/cmake/Curl.cmake | 0 .../cmake/CustomSDSTraits.cmake | 0 cmakeBuild/cmake/DefaultLibNames.cmake | 52 + {build => cmakeBuild}/cmake/Diagnostics.cmake | 0 .../cmake/DisallowOutOfSourceBuilds.cmake | 0 .../cmake/EndpointControllers.cmake | 0 .../cmake/ExtensionPath.cmake | 0 .../cmake/ExternalMediaPlayerAdapters.cmake | 0 {build => cmakeBuild}/cmake/FFmpeg.cmake | 0 .../cmake/GeneratePkgConfig.cmake | 0 .../cmake/KeywordDetector.cmake | 0 .../cmake/LocalDucking.cmake | 0 {build => cmakeBuild}/cmake/Logger.cmake | 0 .../cmake/LowPowerMode.cmake | 0 {build => cmakeBuild}/cmake/MCC.cmake | 0 {build => cmakeBuild}/cmake/MRM.cmake | 0 {build => cmakeBuild}/cmake/MediaPlayer.cmake | 0 {build => cmakeBuild}/cmake/Metrics.cmake | 0 {build => cmakeBuild}/cmake/PCC.cmake | 0 .../cmake/PackageConfigs.cmake | 0 {build => cmakeBuild}/cmake/Platforms.cmake | 0 {build => cmakeBuild}/cmake/PortAudio.cmake | 0 .../cmake/PrepareInstall.cmake | 0 {build => cmakeBuild}/cmake/Rapidjson.cmake | 0 {build => cmakeBuild}/cmake/Rpath.cmake | 0 .../cmake/SpeechEncoder.cmake | 0 {build => cmakeBuild}/cmake/Sqlite.cmake | 0 {build => cmakeBuild}/cmake/TestOptions.cmake | 0 {build => cmakeBuild}/cmake/UseRTTI.cmake | 0 core/CMakeLists.txt | 4 +- .../include/acsdkCore/CoreComponent.h | 5 +- core/acsdkCore/src/CoreComponent.cpp | 15 +- ...tConnectOperationProviderRegistrarTest.cpp | 2 +- core/acsdkSystemClockMonitor/CMakeLists.txt | 5 + .../SystemClockMonitor.h | 63 ++ .../SystemClockNotifier.h | 45 + .../src/CMakeLists.txt | 15 + .../src/SystemClockMonitor.cpp | 56 ++ .../src/SystemClockNotifier.cpp | 27 + .../test/CMakeLists.txt | 3 + .../test/SystemClockMonitorTest.cpp | 88 ++ .../CMakeLists.txt | 16 + .../SystemClockMonitorInterface.h | 41 + .../SystemClockMonitorObserverInterface.h | 13 +- .../SystemClockNotifierInterface.h | 34 + .../acsdkManufactory/ConstructorAdapter.h | 78 -- .../acsdkManufactory/FactorySequencer.h | 63 ++ .../include/acsdkManufactory/Manufactory.h | 14 + .../acsdkManufactory/internal/Component_imp.h | 2 + .../acsdkManufactory/internal/CookBook.h | 93 +- .../acsdkManufactory/internal/CookBook_imp.h | 46 +- .../internal/Manufactory_imp.h | 19 + .../acsdkManufactory/internal/TypeIndex.h | 4 +- shared/acsdkManufactory/src/CookBook.cpp | 36 + shared/acsdkManufactory/test/CMakeLists.txt | 2 +- .../include/acsdkNotifier/Notifier.h | 1 + .../include/acsdkShared/SharedComponent.h | 2 - shared/acsdkShared/src/CMakeLists.txt | 1 + shared/acsdkShared/src/SharedComponent.cpp | 9 +- .../acsdkShutdownManager/ShutdownManager.h | 2 +- .../acsdkShutdownManager/ShutdownNotifier.h | 14 +- .../acsdkShutdownManager/src/CMakeLists.txt | 3 +- .../src/ShutdownNotifier.cpp | 27 + .../CMakeLists.txt | 2 + .../test/CMakeLists.txt | 9 + .../MockShutdownNotifier.h | 48 + .../acsdkStartupManager/StartupNotifier.h | 14 +- shared/acsdkStartupManager/src/CMakeLists.txt | 3 +- .../src/StartupNotifier.cpp | 27 + .../CMakeLists.txt | 2 + tools/Install/mingw.sh | 6 +- tools/Install/pi.sh | 3 +- tools/Install/setup.sh | 15 +- 370 files changed, 8376 insertions(+), 2860 deletions(-) create mode 100644 AVSCommon/AVS/include/AVSCommon/AVS/CapabilityChangeNotifier.h create mode 100644 AVSCommon/AVS/include/AVSCommon/AVS/CapabilityChangeNotifierInterface.h create mode 100644 AVSCommon/AVS/include/AVSCommon/AVS/EditableMessageRequest.h create mode 100644 AVSCommon/AVS/src/EditableMessageRequest.cpp create mode 100644 AVSCommon/AVS/test/EditableMessageRequestTest.cpp create mode 100644 AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilitiesDelegateObserverInterface.h create mode 100644 AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilityConfigurationChangeObserverInterface.h create mode 100644 AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/LocaleAssetsObserverInterface.h delete mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/Timing/SystemClockMonitor.h delete mode 100644 AVSCommon/Utils/src/SystemClockMonitor.cpp rename CapabilitiesDelegate/test/{MockCapabilitiesObserver.h => MockCapabilitiesDelegateObserver.h} (71%) create mode 100644 CapabilityAgents/Alexa/src/AlexaEventProcessedNotifier.cpp create mode 100644 CapabilityAgents/System/test/SystemCapabilityProviderTest.cpp rename applications/acsdkAndroidApplicationAudioPipelineFactory/include/{acsdkAndroidApplicationAudioPipelineFactory => acsdkApplicationAudioPipelineFactory}/AndroidApplicationAudioPipelineFactory.h (72%) create mode 100644 applications/acsdkAndroidApplicationAudioPipelineFactory/include/acsdkApplicationAudioPipelineFactory/ApplicationAudioPipelineFactoryComponent.h create mode 100644 applications/acsdkAndroidApplicationAudioPipelineFactory/src/ApplicationAudioPipelineFactoryComponent.cpp create mode 100644 applications/acsdkBlueZBluetoothImplementation/CMakeLists.txt create mode 100644 applications/acsdkBlueZBluetoothImplementation/include/acsdkBluetoothImplementation/BluetoothImplementationComponent.h create mode 100644 applications/acsdkBlueZBluetoothImplementation/src/BluetoothImplementationComponent.cpp create mode 100644 applications/acsdkBlueZBluetoothImplementation/src/CMakeLists.txt create mode 100644 applications/acsdkCustomApplicationAudioPipelineFactory/include/acsdkApplicationAudioPipelineFactory/ApplicationAudioPipelineFactoryComponent.h rename applications/acsdkCustomApplicationAudioPipelineFactory/include/{acsdkCustomApplicationAudioPipelineFactory => acsdkApplicationAudioPipelineFactory}/CustomApplicationAudioPipelineFactory.h (76%) create mode 100644 applications/acsdkCustomApplicationAudioPipelineFactory/src/ApplicationAudioPipelineFactoryComponent.cpp create mode 100644 applications/acsdkGstreamerApplicationAudioPipelineFactory/include/acsdkApplicationAudioPipelineFactory/ApplicationAudioPipelineFactoryComponent.h rename applications/acsdkGstreamerApplicationAudioPipelineFactory/include/{acsdkGstreamerApplicationAudioPipelineFactory => acsdkApplicationAudioPipelineFactory}/GstreamerApplicationAudioPipelineFactory.h (73%) create mode 100644 applications/acsdkGstreamerApplicationAudioPipelineFactory/src/ApplicationAudioPipelineFactoryComponent.cpp create mode 100644 applications/acsdkNullBluetoothImplementation/CMakeLists.txt create mode 100644 applications/acsdkNullBluetoothImplementation/include/acsdkBluetoothImplementation/BluetoothImplementationComponent.h create mode 100644 applications/acsdkNullBluetoothImplementation/src/BluetoothImplementationComponent.cpp create mode 100644 applications/acsdkNullBluetoothImplementation/src/CMakeLists.txt delete mode 100644 build/cmake/Bluetooth.cmake delete mode 100644 build/cmake/DefaultLibNames.cmake create mode 100644 capabilities/Alerts/acsdkAlertsInterfaces/include/acsdkAlertsInterfaces/AlertsCapabilityAgentInterface.h create mode 100644 capabilities/Bluetooth/acsdkBluetooth/include/acsdkBluetooth/BasicDeviceConnectionRulesProvider.h create mode 100644 capabilities/Bluetooth/acsdkBluetooth/include/acsdkBluetooth/BluetoothComponent.h create mode 100644 capabilities/Bluetooth/acsdkBluetooth/include/acsdkBluetooth/BluetoothNotifier.h create mode 100644 capabilities/Bluetooth/acsdkBluetooth/include/acsdkBluetooth/DeviceConnectionRulesAdapter.h create mode 100644 capabilities/Bluetooth/acsdkBluetooth/src/BasicDeviceConnectionRulesProvider.cpp create mode 100644 capabilities/Bluetooth/acsdkBluetooth/src/BluetoothComponent.cpp create mode 100644 capabilities/Bluetooth/acsdkBluetooth/src/BluetoothNotifier.cpp create mode 100644 capabilities/Bluetooth/acsdkBluetooth/src/DeviceConnectionRulesAdapter.cpp create mode 100644 capabilities/Bluetooth/acsdkBluetoothInterfaces/include/acsdkBluetoothInterfaces/BluetoothDeviceConnectionRulesProviderInterface.h create mode 100644 capabilities/Bluetooth/acsdkBluetoothInterfaces/include/acsdkBluetoothInterfaces/BluetoothNotifierInterface.h rename capabilities/Bluetooth/{acsdkBluetooth/include/acsdkBluetooth => acsdkBluetoothInterfaces/include/acsdkBluetoothInterfaces}/BluetoothStorageInterface.h (93%) create mode 100644 cmakeBuild/BuildDefaults.cmake rename {build => cmakeBuild}/cmake/A4B.cmake (100%) rename {build => cmakeBuild}/cmake/ACSUtils.cmake (100%) rename {build => cmakeBuild}/cmake/Android.cmake (100%) create mode 100644 cmakeBuild/cmake/Bluetooth.cmake rename {build => cmakeBuild}/cmake/BuildOptions.cmake (94%) rename {build => cmakeBuild}/cmake/Captions.cmake (100%) rename {build => cmakeBuild}/cmake/Ccache.cmake (100%) rename {build => cmakeBuild}/cmake/CodeCoverage/CTestCustom.cmake.template (100%) rename {build => cmakeBuild}/cmake/CodeCoverage/CodeCoverage.cmake (100%) rename {build => cmakeBuild}/cmake/CodeCoverage/postCTest.sh (100%) rename {build => cmakeBuild}/cmake/CodeCoverage/preCTest.sh (100%) rename {build => cmakeBuild}/cmake/Comms.cmake (100%) rename {build => cmakeBuild}/cmake/Crypto.cmake (100%) rename {build => cmakeBuild}/cmake/Curl.cmake (100%) rename {build => cmakeBuild}/cmake/CustomSDSTraits.cmake (100%) create mode 100644 cmakeBuild/cmake/DefaultLibNames.cmake rename {build => cmakeBuild}/cmake/Diagnostics.cmake (100%) rename {build => cmakeBuild}/cmake/DisallowOutOfSourceBuilds.cmake (100%) rename {build => cmakeBuild}/cmake/EndpointControllers.cmake (100%) rename {build => cmakeBuild}/cmake/ExtensionPath.cmake (100%) rename {build => cmakeBuild}/cmake/ExternalMediaPlayerAdapters.cmake (100%) rename {build => cmakeBuild}/cmake/FFmpeg.cmake (100%) rename {build => cmakeBuild}/cmake/GeneratePkgConfig.cmake (100%) rename {build => cmakeBuild}/cmake/KeywordDetector.cmake (100%) rename {build => cmakeBuild}/cmake/LocalDucking.cmake (100%) rename {build => cmakeBuild}/cmake/Logger.cmake (100%) rename {build => cmakeBuild}/cmake/LowPowerMode.cmake (100%) rename {build => cmakeBuild}/cmake/MCC.cmake (100%) rename {build => cmakeBuild}/cmake/MRM.cmake (100%) rename {build => cmakeBuild}/cmake/MediaPlayer.cmake (100%) rename {build => cmakeBuild}/cmake/Metrics.cmake (100%) rename {build => cmakeBuild}/cmake/PCC.cmake (100%) rename {build => cmakeBuild}/cmake/PackageConfigs.cmake (100%) rename {build => cmakeBuild}/cmake/Platforms.cmake (100%) rename {build => cmakeBuild}/cmake/PortAudio.cmake (100%) rename {build => cmakeBuild}/cmake/PrepareInstall.cmake (100%) rename {build => cmakeBuild}/cmake/Rapidjson.cmake (100%) rename {build => cmakeBuild}/cmake/Rpath.cmake (100%) rename {build => cmakeBuild}/cmake/SpeechEncoder.cmake (100%) rename {build => cmakeBuild}/cmake/Sqlite.cmake (100%) rename {build => cmakeBuild}/cmake/TestOptions.cmake (100%) rename {build => cmakeBuild}/cmake/UseRTTI.cmake (100%) create mode 100644 core/acsdkSystemClockMonitor/CMakeLists.txt create mode 100644 core/acsdkSystemClockMonitor/include/acsdkSystemClockMonitor/SystemClockMonitor.h create mode 100644 core/acsdkSystemClockMonitor/include/acsdkSystemClockMonitor/SystemClockNotifier.h create mode 100644 core/acsdkSystemClockMonitor/src/CMakeLists.txt create mode 100644 core/acsdkSystemClockMonitor/src/SystemClockMonitor.cpp create mode 100644 core/acsdkSystemClockMonitor/src/SystemClockNotifier.cpp create mode 100644 core/acsdkSystemClockMonitor/test/CMakeLists.txt create mode 100644 core/acsdkSystemClockMonitor/test/SystemClockMonitorTest.cpp create mode 100644 core/acsdkSystemClockMonitorInterfaces/CMakeLists.txt create mode 100644 core/acsdkSystemClockMonitorInterfaces/include/acsdkSystemClockMonitorInterfaces/SystemClockMonitorInterface.h rename {AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces => core/acsdkSystemClockMonitorInterfaces/include/acsdkSystemClockMonitorInterfaces}/SystemClockMonitorObserverInterface.h (68%) create mode 100644 core/acsdkSystemClockMonitorInterfaces/include/acsdkSystemClockMonitorInterfaces/SystemClockNotifierInterface.h delete mode 100644 shared/acsdkManufactory/include/acsdkManufactory/ConstructorAdapter.h create mode 100644 shared/acsdkManufactory/include/acsdkManufactory/FactorySequencer.h create mode 100644 shared/acsdkShutdownManager/src/ShutdownNotifier.cpp create mode 100644 shared/acsdkShutdownManagerInterfaces/test/CMakeLists.txt create mode 100644 shared/acsdkShutdownManagerInterfaces/test/acsdkShutdownManagerInterfaces/MockShutdownNotifier.h create mode 100644 shared/acsdkStartupManager/src/StartupNotifier.cpp diff --git a/ACL/CMakeLists.txt b/ACL/CMakeLists.txt index 9adbdbcba1..4cc82de30a 100644 --- a/ACL/CMakeLists.txt +++ b/ACL/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(ACL LANGUAGES CXX) -include(../build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/ACL/include/ACL/AVSConnectionManager.h b/ACL/include/ACL/AVSConnectionManager.h index a66954ca10..33faa2edec 100644 --- a/ACL/include/ACL/AVSConnectionManager.h +++ b/ACL/include/ACL/AVSConnectionManager.h @@ -176,9 +176,14 @@ class AVSConnectionManager void receive(const std::string& contextId, const std::string& message) override; + std::shared_ptr getMessageRouter() const; + /// Mutex to serialize access to @c m_isEnabled std::mutex m_isEnabledMutex; + /// Mutex to serialize access to @c m_messageRouter + mutable std::mutex m_messageRouterMutex; + /// Internal state to indicate if the Connection object is enabled for making an AVS connection. bool m_isEnabled; diff --git a/ACL/include/ACL/Transport/MessageRequestHandler.h b/ACL/include/ACL/Transport/MessageRequestHandler.h index 5c6f7dc185..c0bc0737fa 100644 --- a/ACL/include/ACL/Transport/MessageRequestHandler.h +++ b/ACL/include/ACL/Transport/MessageRequestHandler.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -142,6 +143,9 @@ class MessageRequestHandler /// The reference to @c PowerResource to prevent device from going into LPM. std::shared_ptr m_powerResource; + + /// Status to be reported back to the @c MessageRequest. + avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status m_resultStatus; }; } // namespace acl diff --git a/ACL/src/AVSConnectionManager.cpp b/ACL/src/AVSConnectionManager.cpp index 142a3eef9f..8396c2ee0a 100644 --- a/ACL/src/AVSConnectionManager.cpp +++ b/ACL/src/AVSConnectionManager.cpp @@ -122,21 +122,37 @@ void AVSConnectionManager::doShutdown() { std::lock_guard lock{m_messageObserverMutex}; m_messageObservers.clear(); } + std::unique_lock lock{m_messageRouterMutex}; + /* There is still a potential deadlock if the reset of m_messageRouter triggers a delete of the message router, + and that delete blocks on code that could call back into AVSConnectionManager and try to acquire that new mutex. + We can get around this by having doShutdown() swap m_messageRouter with a local variable while holding the lock, + and then resetting the local variable after new mutex is released. + */ + auto messageRouter = m_messageRouter; m_messageRouter.reset(); + lock.unlock(); + + messageRouter.reset(); } void AVSConnectionManager::enable() { std::lock_guard lock(m_isEnabledMutex); ACSDK_DEBUG5(LX(__func__)); m_isEnabled = true; - m_messageRouter->enable(); + auto messageRouter = getMessageRouter(); + if (messageRouter) { + messageRouter->enable(); + } } void AVSConnectionManager::disable() { std::lock_guard lock(m_isEnabledMutex); ACSDK_DEBUG5(LX(__func__)); m_isEnabled = false; - m_messageRouter->disable(); + auto messageRouter = getMessageRouter(); + if (messageRouter) { + messageRouter->disable(); + } } bool AVSConnectionManager::isEnabled() { @@ -148,39 +164,76 @@ void AVSConnectionManager::reconnect() { std::lock_guard lock(m_isEnabledMutex); ACSDK_DEBUG5(LX(__func__).d("isEnabled", m_isEnabled)); if (m_isEnabled) { - m_messageRouter->disable(); - m_messageRouter->enable(); + auto messageRouter = getMessageRouter(); + if (messageRouter) { + messageRouter->disable(); + messageRouter->enable(); + } } } void AVSConnectionManager::sendMessage(std::shared_ptr request) { - m_messageRouter->sendMessage(request); + auto messageRouter = getMessageRouter(); + if (messageRouter) { + messageRouter->sendMessage(request); + } else { + ACSDK_WARN(LX("sendMessageFailed") + .d("reason", "nullMessageRouter") + .m("setting status for request to NOT_CONNECTED") + .d("request", request->getJsonContent())); + request->sendCompleted(MessageRequestObserverInterface::Status::NOT_CONNECTED); + } } bool AVSConnectionManager::isConnected() const { - return m_messageRouter->getConnectionStatus().first == ConnectionStatusObserverInterface::Status::CONNECTED; + auto messageRouter = getMessageRouter(); + if (messageRouter) { + return messageRouter->getConnectionStatus().first == ConnectionStatusObserverInterface::Status::CONNECTED; + } + return false; } void AVSConnectionManager::onWakeConnectionRetry() { ACSDK_DEBUG9(LX(__func__)); - m_messageRouter->onWakeConnectionRetry(); + auto messageRouter = getMessageRouter(); + if (messageRouter) { + messageRouter->onWakeConnectionRetry(); + } else { + ACSDK_WARN(LX("onWakeConnectionRetryFailed").d("reason", "nullMessageRouter")); + } } void AVSConnectionManager::setAVSGateway(const std::string& avsGateway) { - m_messageRouter->setAVSGateway(avsGateway); + auto messageRouter = getMessageRouter(); + if (messageRouter) { + messageRouter->setAVSGateway(avsGateway); + } else { + ACSDK_WARN(LX("setAVSGatewayFailed").d("reason", "nullMessageRouter")); + } } std::string AVSConnectionManager::getAVSGateway() const { - return m_messageRouter->getAVSGateway(); + auto messageRouter = getMessageRouter(); + if (messageRouter) { + return messageRouter->getAVSGateway(); + } else { + ACSDK_WARN(LX("getAVSGatewayFailed").d("reason", "nullMessageRouter")); + } + return ""; } void AVSConnectionManager::onConnectionStatusChanged(bool connected) { ACSDK_DEBUG5(LX(__func__).d("connected", connected).d("isEnabled", m_isEnabled)); if (m_isEnabled) { - if (connected) { - m_messageRouter->onWakeConnectionRetry(); + auto messageRouter = getMessageRouter(); + if (messageRouter) { + if (connected) { + messageRouter->onWakeConnectionRetry(); + } else { + messageRouter->onWakeVerifyConnectivity(); + } } else { - m_messageRouter->onWakeVerifyConnectivity(); + ACSDK_WARN(LX("onConnectionStatusChangedFailed").d("reason", "nullMessageRouter")); } } } @@ -228,5 +281,10 @@ void AVSConnectionManager::receive(const std::string& contextId, const std::stri } } +std::shared_ptr AVSConnectionManager::getMessageRouter() const { + std::lock_guard lock{m_messageRouterMutex}; + return m_messageRouter; +} + } // namespace acl } // namespace alexaClientSDK diff --git a/ACL/src/Transport/MessageRequestHandler.cpp b/ACL/src/Transport/MessageRequestHandler.cpp index f5d301bebe..bc035385f1 100644 --- a/ACL/src/Transport/MessageRequestHandler.cpp +++ b/ACL/src/Transport/MessageRequestHandler.cpp @@ -43,7 +43,7 @@ using namespace avsCommon::utils::power; const static std::string AVS_EVENT_URL_PATH_EXTENSION = "/v20160207/events"; /// Boundary for mime encoded requests -const static std::string MIME_BOUNDARY = "WhooHooZeerOoonie!"; +const static std::string MIME_BOUNDARY = "WhooHooZeerOoonie="; /// Timeout for transmission of data on a given stream static const std::chrono::seconds STREAM_PROGRESS_TIMEOUT = std::chrono::seconds(15); @@ -200,7 +200,8 @@ MessageRequestHandler::MessageRequestHandler( m_wasMessageRequestAcknowledgeReported{false}, m_wasMessageRequestFinishedReported{false}, m_responseCode{0}, - m_powerResource{powerResource} { + m_powerResource{powerResource}, + m_resultStatus{MessageRequestObserverInterface::Status::PENDING} { ACSDK_DEBUG7(LX(__func__).d("context", context.get()).d("messageRequest", messageRequest.get())); if (m_powerResource) { @@ -324,8 +325,6 @@ void MessageRequestHandler::onActivity() { bool MessageRequestHandler::onReceiveResponseCode(long responseCode) { ACSDK_DEBUG7(LX(__func__).d("responseCode", responseCode)); - // TODO ACSDK-1839: Provide MessageRequestObserverInterface immediate notification of receipt of response code. - reportMessageRequestAcknowledged(); if (HTTPResponseCode::CLIENT_ERROR_FORBIDDEN == intToHTTPResponseCode(responseCode)) { @@ -333,6 +332,30 @@ bool MessageRequestHandler::onReceiveResponseCode(long responseCode) { } m_responseCode = responseCode; + + // Map HTTPResponseCode values to MessageRequestObserverInterface::Status values. + static const std::unordered_map responseToResult = { + {HTTPResponseCode::HTTP_RESPONSE_CODE_UNDEFINED, MessageRequestObserverInterface::Status::INTERNAL_ERROR}, + {HTTPResponseCode::SUCCESS_OK, MessageRequestObserverInterface::Status::SUCCESS}, + {HTTPResponseCode::SUCCESS_ACCEPTED, MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED}, + {HTTPResponseCode::SUCCESS_NO_CONTENT, MessageRequestObserverInterface::Status::SUCCESS_NO_CONTENT}, + {HTTPResponseCode::CLIENT_ERROR_BAD_REQUEST, MessageRequestObserverInterface::Status::BAD_REQUEST}, + {HTTPResponseCode::CLIENT_ERROR_FORBIDDEN, MessageRequestObserverInterface::Status::INVALID_AUTH}, + {HTTPResponseCode::CLIENT_ERROR_THROTTLING_EXCEPTION, MessageRequestObserverInterface::Status::THROTTLED}, + {HTTPResponseCode::SERVER_ERROR_INTERNAL, MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2}, + {HTTPResponseCode::SERVER_UNAVAILABLE, MessageRequestObserverInterface::Status::REFUSED}}; + + auto responseIterator = responseToResult.find(m_responseCode); + if (responseIterator != responseToResult.end()) { + m_resultStatus = responseIterator->second; + } else { + m_resultStatus = MessageRequestObserverInterface::Status::SERVER_OTHER_ERROR; + } + + ACSDK_DEBUG7(LX("responseCodeTranslated").d("responseStatus", m_resultStatus)); + + m_messageRequest->responseStatusReceived(m_resultStatus); + return true; } @@ -350,51 +373,35 @@ void MessageRequestHandler::onResponseFinished(HTTP2ResponseFinishedStatus statu m_messageRequest->exceptionReceived(nonMimeBody); } - // Hash to allow use of HTTP2ResponseFinishedStatus as the key in an unordered_map. - struct statusHash { - size_t operator()(const HTTP2ResponseFinishedStatus& key) const { - return static_cast(key); - } - }; - - // Mapping HTTP2ResponseFinishedStatus to a MessageRequestObserverInterface::Status. Note that no mapping is - // provided from the COMPLETE status so that the logic below falls through to map the HTTPResponseCode value - // from the completed requests to the appropriate MessageRequestObserverInterface value. - static const std::unordered_map - statusToResult = { - {HTTP2ResponseFinishedStatus::INTERNAL_ERROR, MessageRequestObserverInterface::Status::INTERNAL_ERROR}, - {HTTP2ResponseFinishedStatus::CANCELLED, MessageRequestObserverInterface::Status::CANCELED}, - {HTTP2ResponseFinishedStatus::TIMEOUT, MessageRequestObserverInterface::Status::TIMEDOUT}}; - - // Map HTTPResponseCode values to MessageRequestObserverInterface::Status values. - static const std::unordered_map responseToResult = { - {HTTPResponseCode::HTTP_RESPONSE_CODE_UNDEFINED, MessageRequestObserverInterface::Status::INTERNAL_ERROR}, - {HTTPResponseCode::SUCCESS_OK, MessageRequestObserverInterface::Status::SUCCESS}, - {HTTPResponseCode::SUCCESS_ACCEPTED, MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED}, - {HTTPResponseCode::SUCCESS_NO_CONTENT, MessageRequestObserverInterface::Status::SUCCESS_NO_CONTENT}, - {HTTPResponseCode::CLIENT_ERROR_BAD_REQUEST, MessageRequestObserverInterface::Status::BAD_REQUEST}, - {HTTPResponseCode::CLIENT_ERROR_FORBIDDEN, MessageRequestObserverInterface::Status::INVALID_AUTH}, - {HTTPResponseCode::CLIENT_ERROR_THROTTLING_EXCEPTION, MessageRequestObserverInterface::Status::THROTTLED}, - {HTTPResponseCode::SERVER_ERROR_INTERNAL, MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2}, - {HTTPResponseCode::SERVER_UNAVAILABLE, MessageRequestObserverInterface::Status::REFUSED}}; - - auto result = MessageRequestObserverInterface::Status::INTERNAL_ERROR; + bool receivedResponseCode = MessageRequestObserverInterface::Status::PENDING != m_resultStatus; + + // Map HTTP2ResponseFinishedStatus to a MessageRequestObserverInterface::Status. + + switch (status) { + case HTTP2ResponseFinishedStatus::COMPLETE: + if (!receivedResponseCode) { + m_resultStatus = MessageRequestObserverInterface::Status::INTERNAL_ERROR; + } + break; + case HTTP2ResponseFinishedStatus::TIMEOUT: + m_resultStatus = MessageRequestObserverInterface::Status::TIMEDOUT; + break; + case HTTP2ResponseFinishedStatus::CANCELLED: + m_resultStatus = MessageRequestObserverInterface::Status::CANCELED; + break; + case HTTP2ResponseFinishedStatus::INTERNAL_ERROR: + m_resultStatus = MessageRequestObserverInterface::Status::INTERNAL_ERROR; + break; + default: + ACSDK_ERROR(LX("unhandledHTTP2ResponseFinishedStatus").d("status", status)); + m_resultStatus = MessageRequestObserverInterface::Status::INTERNAL_ERROR; + } - if (HTTP2ResponseFinishedStatus::COMPLETE == status) { - auto responseIterator = responseToResult.find(m_responseCode); - if (responseIterator != responseToResult.end()) { - result = responseIterator->second; - } else { - result = MessageRequestObserverInterface::Status::SERVER_OTHER_ERROR; - } - } else { - auto statusIterator = statusToResult.find(status); - if (statusIterator != statusToResult.end()) { - result = statusIterator->second; - } + if (!receivedResponseCode) { + m_messageRequest->responseStatusReceived(m_resultStatus); } - m_messageRequest->sendCompleted(result); + m_messageRequest->sendCompleted(m_resultStatus); } } // namespace acl diff --git a/ACL/src/Transport/MessageRouter.cpp b/ACL/src/Transport/MessageRouter.cpp index 49819897d1..b085ab2155 100644 --- a/ACL/src/Transport/MessageRouter.cpp +++ b/ACL/src/Transport/MessageRouter.cpp @@ -150,6 +150,13 @@ void MessageRouter::sendMessage(std::shared_ptr request) { ACSDK_ERROR(LX("sendFailed").d("reason", "nullRequest")); return; } + + if (!request->isResolved()) { + ACSDK_ERROR(LX("sendFailed").d("reason", "requestNotReady")); + request->sendCompleted(MessageRequestObserverInterface::Status::BAD_REQUEST); + return; + } + std::unique_lock lock{m_connectionMutex}; if (m_activeTransport) { m_requestQueue->enqueueRequest(request); diff --git a/ACL/test/Transport/HTTP2TransportTest.cpp b/ACL/test/Transport/HTTP2TransportTest.cpp index 7aa1287d8a..82d69addde 100644 --- a/ACL/test/Transport/HTTP2TransportTest.cpp +++ b/ACL/test/Transport/HTTP2TransportTest.cpp @@ -975,8 +975,14 @@ TEST_F(HTTP2TransportTest, test_onSendCompletedNotification) { // Check that we got the right onSendCompleted notifications. for (unsigned messageNum = 0; messageNum < messagesCount; messageNum++) { if (messageObservers[messageNum]->m_status.waitFor(RESPONSE_TIMEOUT)) { - auto expectedMessageObserverStatus = std::get<2>(messageResponseMap[messageNum]); - ASSERT_EQ(messageObservers[messageNum]->m_status.getValue(), expectedMessageObserverStatus); + auto& item = messageResponseMap[messageNum]; + auto responseCode = std::get<0>(item); + auto responseFinished = std::get<1>(item); + auto expectedMessageObserverStatus = std::get<2>(item); + ASSERT_EQ(messageObservers[messageNum]->m_status.getValue(), expectedMessageObserverStatus) + << "messageNum=" << messageNum << " responseCode=" << responseCode + << " responseFinished=" << responseFinished + << " expectedMessageObserverStatus=" << expectedMessageObserverStatus; } } } diff --git a/ADSL/CMakeLists.txt b/ADSL/CMakeLists.txt index 725b0c65fd..77cf0f855d 100644 --- a/ADSL/CMakeLists.txt +++ b/ADSL/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(ADSL LANGUAGES CXX) -include(../build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/AFML/CMakeLists.txt b/AFML/CMakeLists.txt index c8b056b3f8..7152b5aeef 100644 --- a/AFML/CMakeLists.txt +++ b/AFML/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(AFML LANGUAGES CXX) -include(../build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/CapabilityChangeNotifier.h b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilityChangeNotifier.h new file mode 100644 index 0000000000..744dd24b9e --- /dev/null +++ b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilityChangeNotifier.h @@ -0,0 +1,36 @@ +/* + * 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_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYCHANGENOTIFIER_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYCHANGENOTIFIER_H_ + +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { + +/** + * Implementation of CapabilityChangeNotifierInterface + */ +using CapabilityChangeNotifier = + acsdkNotifier::Notifier; + +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYCHANGENOTIFIER_H_ diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/CapabilityChangeNotifierInterface.h b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilityChangeNotifierInterface.h new file mode 100644 index 0000000000..71d82854a0 --- /dev/null +++ b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilityChangeNotifierInterface.h @@ -0,0 +1,36 @@ +/* + * 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_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYCHANGENOTIFIERINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYCHANGENOTIFIERINTERFACE_H_ + +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { + +/** + * Interface for registering to observe capability change. + */ +using CapabilityChangeNotifierInterface = + acsdkNotifierInterfaces::NotifierInterface; + +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYCHANGENOTIFIERINTERFACE_H_ \ No newline at end of file diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/DialogUXStateAggregator.h b/AVSCommon/AVS/include/AVSCommon/AVS/DialogUXStateAggregator.h index f35e2fb39f..5e3587cefe 100644 --- a/AVSCommon/AVS/include/AVSCommon/AVS/DialogUXStateAggregator.h +++ b/AVSCommon/AVS/include/AVSCommon/AVS/DialogUXStateAggregator.h @@ -42,7 +42,6 @@ namespace avs { class DialogUXStateAggregator : public sdkInterfaces::AudioInputProcessorObserverInterface , public sdkInterfaces::SpeechSynthesizerObserverInterface - , public sdkInterfaces::MessageObserverInterface , public sdkInterfaces::ConnectionStatusObserverInterface , public sdkInterfaces::InteractionModelRequestProcessingObserverInterface { public: @@ -113,8 +112,6 @@ class DialogUXStateAggregator const avsCommon::utils::Optional& mediaPlayerState, const std::vector& audioAnalyzerState) override; - void receive(const std::string& contextId, const std::string& message) override; - /// @name InteractionModelRequestProcessingObserverInterface Functions /// @{ void onRequestProcessingStarted() override; @@ -138,13 +135,13 @@ class DialogUXStateAggregator /** * Sets the internal state to @c IDLE if both @c SpeechSynthesizer and @c AudioInputProcessor are in idle state. */ - void tryEnterIdleState(); + void executeTryEnterIdleState(); /** * An event has occurred which may transition @c DialogUXStateAggregator out of THINKING mode. This function * evaluates if the transition is valid and performs the necessary logic to prepare for the transition. */ - void tryExitThinkingState(); + void executeTryExitThinkingState(); /** * Transitions the internal state from THINKING to IDLE. diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/EditableMessageRequest.h b/AVSCommon/AVS/include/AVSCommon/AVS/EditableMessageRequest.h new file mode 100644 index 0000000000..51fc3846cd --- /dev/null +++ b/AVSCommon/AVS/include/AVSCommon/AVS/EditableMessageRequest.h @@ -0,0 +1,61 @@ +/* + * 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_AVS_INCLUDE_AVSCOMMON_AVS_EDITABLEMESSAGEREQUEST_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_EDITABLEMESSAGEREQUEST_H_ + +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { + +/** + * A specialized MessageRequest in which data fields are editable after creation. + */ +class EditableMessageRequest : public avsCommon::avs::MessageRequest { +public: + /** + * Constructor to construct an @c EditableMessageRequest which contains a copy of the data in @c MessageRequest. + * @param messageRequest MessageRequest to copy from + * @note Observers are not considered data and don't get copied by this constructor. + */ + EditableMessageRequest(const MessageRequest& messageRequest); + + /** + * Set json content of the message + * @param json Json content + */ + void setJsonContent(const std::string& json); + + /** + * Set attachment readers of attachment data to be sent to AVS, which will replace the existing readers if there is + * any, and invalid attachment readers will be ignored. + * @param attachmentReaders Vector of named readers + */ + void setAttachmentReaders(const std::vector>& attachmentReaders); + + /** + * Set MessageRequest resolve function. + * @param resolver Resolve function to set + */ + void setMessageRequestResolveFunction(const MessageRequest::MessageRequestResolveFunction& resolver); +}; + +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_EDITABLEMESSAGEREQUEST_H_ diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/MessageRequest.h b/AVSCommon/AVS/include/AVSCommon/AVS/MessageRequest.h index b9c4b2f1fe..c2222dfd19 100644 --- a/AVSCommon/AVS/include/AVSCommon/AVS/MessageRequest.h +++ b/AVSCommon/AVS/include/AVSCommon/AVS/MessageRequest.h @@ -17,6 +17,7 @@ #define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_MESSAGEREQUEST_H_ #include +#include #include #include #include @@ -30,6 +31,8 @@ namespace alexaClientSDK { namespace avsCommon { namespace avs { +class EditableMessageRequest; + /** * This is a wrapper class which allows a client to send a Message to AVS, and be notified when the attempt to * send the Message was completed. @@ -56,6 +59,16 @@ class MessageRequest { std::shared_ptr reader; }; + /** + * Function to resolve an editable message request based on the provided resolveKey by updating the MessageRequest. + * @param[in,out] req Target editable request message that will be modified in place. + * @param resolveKey Key used to resolve the message request + * @return @c true if resolving successfully, else @ false + * @note This function need to be thread-safe, and is allowed to block. + */ + using MessageRequestResolveFunction = + std::function& req, const std::string& resolveKey)>; + /** * Constructor. * @@ -73,12 +86,25 @@ class MessageRequest { * @param uriPathExtension An optional uri path extension which will be appended to the base url of the AVS. * @param headers key/value pairs of extra HTTP headers to use with this request. * endpoint. If not specified, the default AVS path extension should be used by the sender implementation. + * @param resolver Function to resolve message. Null if message doesn't need resolving. Resolving function aims to + * support the use case that one message request will be sent to multiple places with some fields having different + * values for different destinations. In such use cases, @c MessageRequest works as a container with all required + * info to build different versions of requests. The resolving function contains the logic to build the target + * message request based on the info in the original request, and provided resolveKey. */ MessageRequest( const std::string& jsonContent, bool isSerialized, const std::string& uriPathExtension = "", - std::vector> headers = {}); + std::vector> headers = {}, + MessageRequestResolveFunction resolver = nullptr); + + /** + * Constructor to construct a MessageRequest which contains a copy of the data in @c MessageRequest. + * @param messageRequest MessageRequest to copy from + * @note Observers are not considered data and don't get copied by this constructor. + */ + MessageRequest(const MessageRequest& messageRequest); /** * Destructor. @@ -131,7 +157,14 @@ class MessageRequest { * @return @c NamedReader of the ith attachment in the message. * A null pointer is returned when @c index is out of bound. */ - std::shared_ptr getAttachmentReader(size_t index); + std::shared_ptr getAttachmentReader(size_t index) const; + + /** + * Called when the Response code is received. + * + * @param status The status of the response that was received. + */ + virtual void responseStatusReceived(avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status status); /** * This is called once the send request has completed. The status parameter indicates success or failure. @@ -167,6 +200,19 @@ class MessageRequest { */ const std::vector>& getHeaders() const; + /** + * Check whether message is resolved and ready to send. + * @return @c true if message is already resolved, else @c false + */ + bool isResolved() const; + + /** + * Resolve message to a valid message by updating the content of the message based on provided resolveKey + * @param resolveKey Key used to resolve message + * @return New resolved MessageRequest + */ + std::shared_ptr resolveRequest(const std::string& resolveKey) const; + protected: /// Mutex to guard access of m_observers. std::mutex m_observerMutex; @@ -188,6 +234,9 @@ class MessageRequest { /// Optional headers to send with this request to AVS. std::vector> m_headers; + + /// Resolver function to resolve current message request to a valid state. Null if message is already resolved. + MessageRequestResolveFunction m_resolver; }; } // namespace avs diff --git a/AVSCommon/AVS/src/AVSDirective.cpp b/AVSCommon/AVS/src/AVSDirective.cpp index 3c3419aa91..6704948063 100644 --- a/AVSCommon/AVS/src/AVSDirective.cpp +++ b/AVSCommon/AVS/src/AVSDirective.cpp @@ -292,6 +292,10 @@ std::unique_ptr AVSDirective::create( std::unique_ptr AVSDirective::getAttachmentReader( const std::string& contentId, sds::ReaderPolicy readerPolicy) const { + if (!m_attachmentManager) { + ACSDK_ERROR(LX("getAttachmentReaderFailed").d("reason", "nullAttachmentManager")); + return nullptr; + } auto attachmentId = m_attachmentManager->generateAttachmentId(m_attachmentContextId, contentId); return m_attachmentManager->createReader(attachmentId, readerPolicy); } diff --git a/AVSCommon/AVS/src/Attachment/AttachmentManager.cpp b/AVSCommon/AVS/src/Attachment/AttachmentManager.cpp index 8d4e23e9ed..d61974fc4b 100644 --- a/AVSCommon/AVS/src/Attachment/AttachmentManager.cpp +++ b/AVSCommon/AVS/src/Attachment/AttachmentManager.cpp @@ -80,6 +80,7 @@ std::string AttachmentManager::generateAttachmentId(const std::string& contextId bool AttachmentManager::setAttachmentTimeoutMinutes(std::chrono::minutes minutes) { if (minutes < ATTACHMENT_MANAGER_TIMOUT_MINUTES_MINIMUM) { + /* coverity[dead_error_line] */ int minimumMinutes = ATTACHMENT_MANAGER_TIMOUT_MINUTES_MINIMUM.count(); std::string minutePrintString = (1 == minimumMinutes) ? " minute" : " minutes"; ACSDK_ERROR(LX("setAttachmentTimeoutError") diff --git a/AVSCommon/AVS/src/CapabilityResources.cpp b/AVSCommon/AVS/src/CapabilityResources.cpp index 8905b8ed99..c81f56212b 100644 --- a/AVSCommon/AVS/src/CapabilityResources.cpp +++ b/AVSCommon/AVS/src/CapabilityResources.cpp @@ -84,12 +84,11 @@ bool CapabilityResources::addFriendlyNameWithText( CapabilityResources::FriendlyName( {text, utils::Optional(locale)})) != m_items.end()) { - ACSDK_ERROR(LX("addFriendlyNameWithTextFailed") - .d("reason", "duplicateText") - .sensitive("text", text) - .sensitive("locale", locale)); - m_isValid = false; - return false; + ACSDK_WARN(LX("addFriendlyNameWithTextSucceeded") + .d("reason", "duplicateText") + .sensitive("text", text) + .sensitive("locale", locale)); + return true; } m_items.push_back({text, utils::Optional(locale)}); diff --git a/AVSCommon/AVS/src/DialogUXStateAggregator.cpp b/AVSCommon/AVS/src/DialogUXStateAggregator.cpp index 57c2e89ba5..ebd5b428f5 100644 --- a/AVSCommon/AVS/src/DialogUXStateAggregator.cpp +++ b/AVSCommon/AVS/src/DialogUXStateAggregator.cpp @@ -107,12 +107,13 @@ void DialogUXStateAggregator::removeObserver(std::shared_ptr& mediaPlayerState, const std::vector& audioAnalyzerState) { + ACSDK_DEBUG0(LX(__func__).d("SpeechSynthesizerState", state)); m_speechSynthesizerState = state; - m_executor.submit([this, state]() { + ACSDK_DEBUG0(LX("onStateChangedLambda").d("SpeechSynthesizerState", state)); switch (state) { case SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING: onActivityStarted(); @@ -154,7 +156,7 @@ void DialogUXStateAggregator::onStateChanged( return; case SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED: case SpeechSynthesizerObserverInterface::SpeechSynthesizerState::INTERRUPTED: - tryEnterIdleState(); + executeTryEnterIdleState(); return; case SpeechSynthesizerObserverInterface::SpeechSynthesizerState::LOSING_FOCUS: return; @@ -166,28 +168,21 @@ void DialogUXStateAggregator::onStateChanged( }); } -void DialogUXStateAggregator::receive(const std::string& contextId, const std::string& message) { - tryExitThinkingState(); -} - -void DialogUXStateAggregator::tryExitThinkingState() { - m_executor.submit([this]() { - if (DialogUXStateObserverInterface::DialogUXState::THINKING == m_currentState && - SpeechSynthesizerObserverInterface::SpeechSynthesizerState::GAINING_FOCUS != m_speechSynthesizerState) { - ACSDK_DEBUG5( - LX("Kicking off short timer").d("shortTimeout in ms", m_shortTimeoutForThinkingToIdle.count())); - /* - * Stop the long timer and start a short timer so that either the state will change (i.e. Speech begins) - * or we automatically go to idle after the short timeout (i.e. the directive received isn't related to - * speech, like a setVolume directive). Cannot automatically goto IDLE because it will cause - * SpeechSynthesizer to release focus, which may happen before the Speak has rendered. - */ - m_thinkingTimeoutTimer.stop(); - m_thinkingTimeoutTimer.start( - m_shortTimeoutForThinkingToIdle, - std::bind(&DialogUXStateAggregator::transitionFromThinkingTimedOut, this)); - } - }); +void DialogUXStateAggregator::executeTryExitThinkingState() { + ACSDK_DEBUG0(LX(__func__)); + if (DialogUXStateObserverInterface::DialogUXState::THINKING == m_currentState && + SpeechSynthesizerObserverInterface::SpeechSynthesizerState::GAINING_FOCUS != m_speechSynthesizerState) { + ACSDK_DEBUG5(LX("Kicking off short timer").d("shortTimeout in ms", m_shortTimeoutForThinkingToIdle.count())); + /* + * Stop the long timer and start a short timer so that either the state will change (i.e. Speech begins) + * or we automatically go to idle after the short timeout (i.e. the directive received isn't related to + * speech, like a setVolume directive). Cannot automatically goto IDLE because it will cause + * SpeechSynthesizer to release focus, which may happen before the Speak has rendered. + */ + m_thinkingTimeoutTimer.stop(); + m_thinkingTimeoutTimer.start( + m_shortTimeoutForThinkingToIdle, std::bind(&DialogUXStateAggregator::transitionFromThinkingTimedOut, this)); + } } void DialogUXStateAggregator::onConnectionStatusChanged( @@ -201,13 +196,11 @@ void DialogUXStateAggregator::onConnectionStatusChanged( } void DialogUXStateAggregator::onRequestProcessingStarted() { - ACSDK_DEBUG(LX("onRequestProcessingStarted")); + ACSDK_DEBUG0(LX("onRequestProcessingStarted")); m_executor.submit([this]() { + ACSDK_DEBUG0(LX("onRequestProcessingStartedLambda").d("currentState", m_currentState)); // Stop the listening timer m_listeningTimeoutTimer.stop(); - - ACSDK_DEBUG0(LX("onRequestProcessingStartedLambda").d("currentState", m_currentState)); - switch (m_currentState) { // IDLE is included for the theoretical edgecase that RPS is received after the listening timeout occurs. case DialogUXStateObserverInterface::DialogUXState::IDLE: @@ -233,9 +226,16 @@ void DialogUXStateAggregator::onRequestProcessingStarted() { } void DialogUXStateAggregator::onRequestProcessingCompleted() { - // If receive() calls occur before onRequestProcessStarted() happens, we need to use this method as a fallback to - // exit THINKING mode. - tryExitThinkingState(); + ACSDK_DEBUG(LX("onRequestProcessingCompleted")); + m_executor.submit([this]() { + if (DialogUXStateObserverInterface::DialogUXState::LISTENING == m_currentState) { + /// It is possible that the cloud sends RPC without sending RPS. In those situations, if we are in + /// LISTENING state, switch back to IDLE. + executeSetState(DialogUXStateObserverInterface::DialogUXState::IDLE); + } else { + executeTryExitThinkingState(); + } + }); } void DialogUXStateAggregator::notifyObserversOfState() { @@ -315,7 +315,7 @@ bool DialogUXStateAggregator::executeSetState(sdkInterfaces::DialogUXStateObserv return true; } -void DialogUXStateAggregator::tryEnterIdleState() { +void DialogUXStateAggregator::executeTryEnterIdleState() { ACSDK_DEBUG5(LX(__func__)); m_thinkingTimeoutTimer.stop(); m_multiturnSpeakingToListeningTimer.stop(); diff --git a/AVSCommon/AVS/src/EditableMessageRequest.cpp b/AVSCommon/AVS/src/EditableMessageRequest.cpp new file mode 100644 index 0000000000..82d2e3abcf --- /dev/null +++ b/AVSCommon/AVS/src/EditableMessageRequest.cpp @@ -0,0 +1,60 @@ +/* + * 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/AVS/EditableMessageRequest.h" +#include "AVSCommon/Utils/Logger/Logger.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { + +using namespace avsCommon::sdkInterfaces; + +/// String to identify log entries originating from this file. +static const std::string TAG("EditableMessageRequest"); + +/** + * 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) + +EditableMessageRequest::EditableMessageRequest(const MessageRequest& messageRequest) : + avsCommon::avs::MessageRequest(messageRequest) { +} + +void EditableMessageRequest::setJsonContent(const std::string& json) { + m_jsonContent = json; +} + +void EditableMessageRequest::setAttachmentReaders(const std::vector>& attachmentReaders) { + m_readers.clear(); + for (auto& it : attachmentReaders) { + if (it) { + addAttachmentReader(it->name, it->reader); + } else { + ACSDK_ERROR(LX("Failed to set attachment readers").d("reason", "nullAttachment")); + } + } +} + +void EditableMessageRequest::setMessageRequestResolveFunction( + const MessageRequest::MessageRequestResolveFunction& resolver) { + m_resolver = resolver; +} +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/AVS/src/MessageRequest.cpp b/AVSCommon/AVS/src/MessageRequest.cpp index 6f3efbb61b..d91f94866f 100644 --- a/AVSCommon/AVS/src/MessageRequest.cpp +++ b/AVSCommon/AVS/src/MessageRequest.cpp @@ -14,6 +14,7 @@ */ #include "AVSCommon/AVS/MessageRequest.h" +#include "AVSCommon/AVS/EditableMessageRequest.h" #include "AVSCommon/Utils/Logger/Logger.h" namespace alexaClientSDK { @@ -42,11 +43,22 @@ MessageRequest::MessageRequest( const std::string& jsonContent, bool isSerialized, const std::string& uriPathExtension, - std::vector> headers) : + std::vector> headers, + MessageRequestResolveFunction resolver) : m_jsonContent{jsonContent}, m_isSerialized{isSerialized}, m_uriPathExtension{uriPathExtension}, - m_headers(std::move(headers)) { + m_headers(std::move(headers)), + m_resolver{resolver} { +} + +MessageRequest::MessageRequest(const MessageRequest& messageRequest) : + m_jsonContent{messageRequest.m_jsonContent}, + m_isSerialized{messageRequest.m_isSerialized}, + m_uriPathExtension{messageRequest.m_uriPathExtension}, + m_readers{messageRequest.m_readers}, + m_headers{messageRequest.m_headers}, + m_resolver{messageRequest.m_resolver} { } MessageRequest::~MessageRequest() { @@ -80,7 +92,7 @@ int MessageRequest::attachmentReadersCount() const { return m_readers.size(); } -std::shared_ptr MessageRequest::getAttachmentReader(size_t index) { +std::shared_ptr MessageRequest::getAttachmentReader(size_t index) const { if (m_readers.size() <= index) { ACSDK_ERROR(LX("getAttachmentReaderFailed").d("reason", "index out of bound").d("index", index)); return nullptr; @@ -89,6 +101,16 @@ std::shared_ptr MessageRequest::getAttachmentReader return m_readers[index]; } +void MessageRequest::responseStatusReceived(avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status status) { + std::unique_lock lock{m_observerMutex}; + auto observers = m_observers; + lock.unlock(); + + for (auto observer : observers) { + observer->onResponseStatusReceived(status); + } +} + void MessageRequest::sendCompleted(avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status status) { std::unique_lock lock{m_observerMutex}; auto observers = m_observers; @@ -136,6 +158,25 @@ const std::vector>& MessageRequest::getHeade return m_headers; } +bool MessageRequest::isResolved() const { + return !m_resolver; +} + +std::shared_ptr MessageRequest::resolveRequest(const std::string& resolveKey) const { + if (m_resolver) { + auto editableReq = std::make_shared(*this); + if (m_resolver(editableReq, resolveKey)) { + // Set MessageRequest to resolved state + editableReq->setMessageRequestResolveFunction(nullptr); + return editableReq; + } + ACSDK_ERROR(LX("Failed to resolve MessageRequest.")); + return nullptr; + } + ACSDK_ERROR(LX("ResolveRequest is called for a resolved MessageRequest.")); + return nullptr; +} + } // namespace avs } // namespace avsCommon } // namespace alexaClientSDK diff --git a/AVSCommon/AVS/test/AVSDirectiveTest.cpp b/AVSCommon/AVS/test/AVSDirectiveTest.cpp index e29176b313..3ad5153f94 100644 --- a/AVSCommon/AVS/test/AVSDirectiveTest.cpp +++ b/AVSCommon/AVS/test/AVSDirectiveTest.cpp @@ -167,6 +167,31 @@ TEST(AVSDirectiveTest, test_parseWithEndpointCookie) { EXPECT_EQ(endpoint.cookies["key"], "value"); } +TEST(AVSDirectiveTest, test_getAttachementReaderCallIfAttachementManagerIsNullptr) { + // clang-format off + std::string directiveJson = R"({ + "directive": { + "header": { + "namespace": "Namespace", + "name": "Name", + "messageId": "Id", + "correlationToken": "Token123", + "eventCorrelationToken": "Event123" + }, + "payload": { + "key":"value" + } + }})"; + // clang-format on + + auto parseResult = AVSDirective::create(directiveJson, nullptr, ""); + EXPECT_EQ(parseResult.second, AVSDirective::ParseStatus::SUCCESS); + ASSERT_THAT(parseResult.first, NotNull()); + + auto& directive = *parseResult.first; + ASSERT_THAT(directive.getAttachmentReader("Token123", sds::ReaderPolicy::NONBLOCKING), IsNull()); +} + } // namespace test } // namespace avs } // namespace avsCommon diff --git a/AVSCommon/AVS/test/CapabilityResourcesTest.cpp b/AVSCommon/AVS/test/CapabilityResourcesTest.cpp index 7c8361705e..27605376d1 100644 --- a/AVSCommon/AVS/test/CapabilityResourcesTest.cpp +++ b/AVSCommon/AVS/test/CapabilityResourcesTest.cpp @@ -39,6 +39,11 @@ static std::string expectedFriendlyNamesJson = R"({"@type":"text","value":{"text":"air conditioner","locale":"en-US"}},)" R"({"@type":"asset","value":{"assetId":"Alexa.Setting.Temperature"}}])" R"(})"; +/// The expected friendly names json with a single value. +static std::string expectedSimpleFriendlyNameJson = R"({)" + R"("friendlyNames":[)" + R"({"@type":"text","value":{"text":"fan","locale":"en-US"}}])" + R"(})"; /** * The test harness for @c CapabilityResources. @@ -100,14 +105,15 @@ TEST_F(CapabilityResourcesTest, test_addFriendlyNameWithEmptyLocale) { } /** - * Test if the addFriendlyNameWithText method checks for duplicate entries. + * Test if the addFriendlyNameWithText method checks for duplicate entries. It should succeed but skip the duplicate. */ TEST_F(CapabilityResourcesTest, test_addFriendlyNameWithDuplicateText) { CapabilityResources capabilityResources; ASSERT_TRUE(capabilityResources.addFriendlyNameWithText(FAN_FRIENDLY_NAME, TEST_LOCALE)); - ASSERT_FALSE(capabilityResources.addFriendlyNameWithText(FAN_FRIENDLY_NAME, TEST_LOCALE)); - ASSERT_FALSE(capabilityResources.isValid()); - ASSERT_EQ(capabilityResources.toJson(), "{}"); + ASSERT_TRUE(capabilityResources.isValid()); + ASSERT_TRUE(capabilityResources.addFriendlyNameWithText(FAN_FRIENDLY_NAME, TEST_LOCALE)); + ASSERT_TRUE(capabilityResources.isValid()); + ASSERT_EQ(capabilityResources.toJson(), expectedSimpleFriendlyNameJson); } /** diff --git a/AVSCommon/AVS/test/DialogUXStateAggregatorTest.cpp b/AVSCommon/AVS/test/DialogUXStateAggregatorTest.cpp index 3d4ec15f55..c85cbcb822 100644 --- a/AVSCommon/AVS/test/DialogUXStateAggregatorTest.cpp +++ b/AVSCommon/AVS/test/DialogUXStateAggregatorTest.cpp @@ -288,15 +288,15 @@ TEST_F(DialogUXAggregatorTest, test_thinkingThenReceiveGoesToIdleAfterLongTimeou m_aggregator->onRequestProcessingStarted(); assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::THINKING); - m_aggregator->receive("", ""); + m_aggregator->onRequestProcessingCompleted(); assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::IDLE, TRANSITION_TIMEOUT); } /** - * Tests that the LISTENING state goes to SPEAKING but not IDLE after both a message is received and a SpeechSynthesizer - * speak state is received. + * Tests that the LISTENING state goes to SPEAKING but not IDLE after both RequestProcessingStarted and + * RequestProcessingCompleted are received followed by SpeechSynthesizer PLAYING. */ -TEST_F(DialogUXAggregatorTest, test_listeningThenReceiveThenSpeakGoesToSpeakButNotIdle) { +TEST_F(DialogUXAggregatorTest, test_listeningThenRequestProcessingCompletedThenSpeakGoesToSpeakButNotIdle) { assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::IDLE); m_aggregator->onStateChanged(AudioInputProcessorObserverInterface::State::BUSY); @@ -305,7 +305,7 @@ TEST_F(DialogUXAggregatorTest, test_listeningThenReceiveThenSpeakGoesToSpeakButN m_aggregator->onRequestProcessingStarted(); assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::THINKING); - m_aggregator->receive("", ""); + m_aggregator->onRequestProcessingCompleted(); m_aggregator->onStateChanged( SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING, @@ -327,14 +327,13 @@ TEST_F(DialogUXAggregatorTest, test_speakingAndRecognizingFinishedGoesToIdle) { m_aggregator->onRequestProcessingStarted(); assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::THINKING); - m_aggregator->receive("", ""); + m_aggregator->onRequestProcessingCompleted(); m_aggregator->onStateChanged( SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING, TEST_SOURCE_ID, m_testMediaPlayerState, m_testAudioAnalyzerState); - assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::SPEAKING); m_aggregator->onStateChanged(AudioInputProcessorObserverInterface::State::IDLE); @@ -395,7 +394,7 @@ TEST_F(DialogUXAggregatorTest, test_speakingFinishedDoesNotGoesToIdleImmediately m_aggregator->onRequestProcessingStarted(); assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::THINKING); - m_aggregator->receive("", ""); + m_aggregator->onRequestProcessingCompleted(); m_aggregator->onStateChanged( SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING, @@ -414,13 +413,13 @@ TEST_F(DialogUXAggregatorTest, test_speakingFinishedDoesNotGoesToIdleImmediately assertNoStateChange(m_testObserver); } -/// Tests that a simple message receive does nothing. +/// Test that requestProcessingCompleted while SPEAKING does nothing. TEST_F(DialogUXAggregatorTest, test_simpleReceiveDoesNothing) { assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::IDLE); - m_aggregator->receive("", ""); + m_aggregator->onRequestProcessingStarted(); - assertNoStateChange(m_testObserver); + assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::THINKING); m_aggregator->onStateChanged( SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING, @@ -430,7 +429,7 @@ TEST_F(DialogUXAggregatorTest, test_simpleReceiveDoesNothing) { assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::SPEAKING); - m_aggregator->receive("", ""); + m_aggregator->onRequestProcessingCompleted(); assertNoStateChange(m_testObserver); } @@ -445,7 +444,7 @@ TEST_F(DialogUXAggregatorTest, test_thinkingThenReceiveRemainsInThinkingIfSpeech m_aggregator->onRequestProcessingStarted(); assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::THINKING); - m_aggregator->receive("", ""); + m_aggregator->onRequestProcessingCompleted(); m_aggregator->onStateChanged( SpeechSynthesizerObserverInterface::SpeechSynthesizerState::GAINING_FOCUS, @@ -454,8 +453,6 @@ TEST_F(DialogUXAggregatorTest, test_thinkingThenReceiveRemainsInThinkingIfSpeech m_testAudioAnalyzerState); // Make sure after SpeechSynthesizer reports GAINING_FOCUS, that it would stay in THINKING state - m_aggregator->receive("", ""); - assertNoStateChange(m_testObserver, TRANSITION_TIMEOUT); } @@ -465,14 +462,14 @@ TEST_F(DialogUXAggregatorTest, test_validStatesForRPSToThinking) { m_aggregator->onRequestProcessingStarted(); assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::THINKING); - m_aggregator->receive("", ""); + m_aggregator->onRequestProcessingCompleted(); assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::IDLE); m_aggregator->onStateChanged(AudioInputProcessorObserverInterface::State::BUSY); assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::LISTENING); m_aggregator->onRequestProcessingStarted(); assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::THINKING); - m_aggregator->receive("", ""); + m_aggregator->onRequestProcessingCompleted(); assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::IDLE); m_aggregator->onStateChanged( SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING, @@ -497,20 +494,18 @@ TEST_F(DialogUXAggregatorTest, test_validStatesForRPSToThinking) { assertNoStateChange(m_testObserver); } -/// Test that if receive() happens before RequestProcessingCompleted directive is handled, we exit THINKING mode. +/// Test that if RequestProcessingCompleted directive is handled, we exit THINKING mode. TEST_F(DialogUXAggregatorTest, test_receiveThenRPCTransitionsOutOfThinking) { assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::IDLE); m_aggregator->onStateChanged(AudioInputProcessorObserverInterface::State::BUSY); assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::LISTENING); - m_aggregator->receive("", ""); - m_aggregator->receive("", ""); - m_aggregator->onRequestProcessingStarted(); - assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::THINKING); m_aggregator->onRequestProcessingCompleted(); + assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::THINKING); + assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::IDLE); } @@ -529,6 +524,21 @@ TEST_F(DialogUXAggregatorTest, test_receiveAIPBusyAfterRPS) { assertNoStateChange(m_testObserver); } +/// Test that we transition from LISTENING to IDLE when RequestProcessingCompleted is received without +/// RequestProcessingStarted. +TEST_F(DialogUXAggregatorTest, test_receiveRPCwithoutRPS) { + assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::IDLE); + + m_aggregator->onStateChanged(AudioInputProcessorObserverInterface::State::RECOGNIZING); + assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::LISTENING); + + m_aggregator->onStateChanged(AudioInputProcessorObserverInterface::State::BUSY); + assertNoStateChange(m_testObserver); + + m_aggregator->onRequestProcessingCompleted(); + assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::IDLE); +} + } // namespace test } // namespace avsCommon } // namespace alexaClientSDK diff --git a/AVSCommon/AVS/test/EditableMessageRequestTest.cpp b/AVSCommon/AVS/test/EditableMessageRequestTest.cpp new file mode 100644 index 0000000000..c7250a345c --- /dev/null +++ b/AVSCommon/AVS/test/EditableMessageRequestTest.cpp @@ -0,0 +1,101 @@ +/* + * 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 "AVSCommon/AVS/EditableMessageRequest.h" + +using namespace ::testing; + +namespace alexaClientSDK { + +namespace avsCommon { +namespace avs { +namespace test { + +class MockAttachmentReader : public attachment::AttachmentReader { +public: + MOCK_METHOD4( + read, + std::size_t( + void* buf, + std::size_t numBytes, + attachment::AttachmentReader::ReadStatus* readStatus, + std::chrono::milliseconds timeoutMs)); + MOCK_METHOD1(seek, bool(uint64_t offset)); + MOCK_METHOD0(getNumUnreadBytes, uint64_t()); + MOCK_METHOD1(close, void(attachment::AttachmentReader::ClosePoint closePoint)); +}; + +class EditableMessageRequestTest : public ::testing::Test {}; + +TEST_F(EditableMessageRequestTest, test_setJsonContent) { + std::string jsonContent = "{\"name\": \"value\"}"; + MessageRequest sourceRequest("{}", true, "", {}); + EditableMessageRequest messageRequest = sourceRequest; + + EXPECT_NE(jsonContent, messageRequest.getJsonContent()); + messageRequest.setJsonContent(jsonContent); + EXPECT_EQ(jsonContent, messageRequest.getJsonContent()); +} + +TEST_F(EditableMessageRequestTest, test_setAttachmentReaders) { + std::shared_ptr reader = std::make_shared(); + std::vector> attachmentReaders{ + std::make_shared("Test", reader)}; + MessageRequest sourceRequest("{}", true, "", {}); + EditableMessageRequest messageRequest = sourceRequest; + + EXPECT_EQ(0, messageRequest.attachmentReadersCount()); + messageRequest.setAttachmentReaders(attachmentReaders); + EXPECT_EQ(1, messageRequest.attachmentReadersCount()); + auto namedReader = messageRequest.getAttachmentReader(0); + EXPECT_TRUE(namedReader != nullptr); + EXPECT_EQ(attachmentReaders[0]->name, namedReader->name); + EXPECT_EQ(attachmentReaders[0]->reader, namedReader->reader); +} + +TEST_F(EditableMessageRequestTest, test_setAttachmentReadersFails) { + auto reader = std::make_shared(); + auto validNamedReader = std::make_shared("Test", reader); + std::vector> attachmentReaders{ + validNamedReader, nullptr, std::make_shared("Test2", nullptr)}; + MessageRequest sourceRequest("{}", true, "", {}); + EditableMessageRequest messageRequest = sourceRequest; + + EXPECT_EQ(0, messageRequest.attachmentReadersCount()); + messageRequest.setAttachmentReaders(attachmentReaders); + EXPECT_EQ(1, messageRequest.attachmentReadersCount()); + auto namedReader = messageRequest.getAttachmentReader(0); + EXPECT_TRUE(namedReader != nullptr); + EXPECT_EQ(attachmentReaders[0]->name, namedReader->name); + EXPECT_EQ(attachmentReaders[0]->reader, namedReader->reader); +} + +TEST_F(EditableMessageRequestTest, test_setMessageRequestResolveFunction) { + MessageRequest sourceRequest("{}", true, "", {}); + EditableMessageRequest request = sourceRequest; + MockFunction&, std::string)> mockResolverFunc; + + EXPECT_TRUE(request.isResolved()); + request.setMessageRequestResolveFunction(mockResolverFunc.AsStdFunction()); + EXPECT_FALSE(request.isResolved()); +} + +} // namespace test +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/AVS/test/MessageRequestTest.cpp b/AVSCommon/AVS/test/MessageRequestTest.cpp index d6a2177145..1f5ddd8ced 100644 --- a/AVSCommon/AVS/test/MessageRequestTest.cpp +++ b/AVSCommon/AVS/test/MessageRequestTest.cpp @@ -13,9 +13,11 @@ * permissions and limitations under the License. */ +#include #include #include "AVSCommon/AVS/MessageRequest.h" +#include "AVSCommon/AVS/EditableMessageRequest.h" using namespace ::testing; @@ -25,8 +27,55 @@ namespace avsCommon { namespace avs { namespace test { +class MockAttachmentReader : public attachment::AttachmentReader { +public: + MOCK_METHOD4( + read, + std::size_t( + void* buf, + std::size_t numBytes, + attachment::AttachmentReader::ReadStatus* readStatus, + std::chrono::milliseconds timeoutMs)); + MOCK_METHOD1(seek, bool(uint64_t offset)); + MOCK_METHOD0(getNumUnreadBytes, uint64_t()); + MOCK_METHOD1(close, void(attachment::AttachmentReader::ClosePoint closePoint)); +}; + +class MockMessageRequestObserver : public avsCommon::sdkInterfaces::MessageRequestObserverInterface { +public: + MOCK_METHOD1(onResponseStatusReceived, void(MessageRequestObserverInterface::Status status)); + MOCK_METHOD1(onSendCompleted, void(MessageRequestObserverInterface::Status status)); + MOCK_METHOD1(onExceptionReceived, void(const std::string& exceptionMessage)); +}; + class MessageRequestTest : public ::testing::Test {}; +/// Test copy constructor +TEST_F(MessageRequestTest, test_copyConstructor) { + std::string jsonContent = "{\"name\": \"value\"}"; + std::shared_ptr attachmentReader = std::make_shared(); + std::string uri = "/test/uri"; + MockFunction&, std::string)> mockResolverFunc; + auto mockMessageRequestObserver = std::make_shared(); + MessageRequest request{ + jsonContent, true, uri, std::vector>{}, mockResolverFunc.AsStdFunction()}; + request.addAttachmentReader("reader", attachmentReader); + request.addObserver(mockMessageRequestObserver); + + auto copiedReq = request; + + EXPECT_EQ(jsonContent, copiedReq.getJsonContent()); + EXPECT_EQ(uri, copiedReq.getUriPathExtension()); + EXPECT_FALSE(copiedReq.isResolved()); + EXPECT_EQ(1, copiedReq.attachmentReadersCount()); + EXPECT_EQ(request.getAttachmentReader(0), copiedReq.getAttachmentReader(0)); + + // Verify that observers are not copied in copy constructor. + EXPECT_CALL(*mockMessageRequestObserver, onSendCompleted(_)).Times(1); + request.sendCompleted(sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS); + copiedReq.sendCompleted(sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS); +} + /** * Test functionality of adding extra headers */ @@ -38,6 +87,37 @@ TEST_F(MessageRequestTest, test_extraHeaders) { EXPECT_EQ(expected, actual); } +TEST_F(MessageRequestTest, test_isResolved) { + MessageRequest resolvedReq("{}", true, ""); + EXPECT_TRUE(resolvedReq.isResolved()); + + MockFunction&, std::string)> mockResolverFunc; + MessageRequest unresolvedReq{ + "{}", true, "", std::vector>{}, mockResolverFunc.AsStdFunction()}; + EXPECT_FALSE(unresolvedReq.isResolved()); +} + +TEST_F(MessageRequestTest, test_resolveRequestFails) { + MessageRequest resolvedReq("{}", true, ""); + EXPECT_TRUE(nullptr == resolvedReq.resolveRequest("")); +} + +TEST_F(MessageRequestTest, test_resolveRequest) { + MockFunction&, std::string)> mockResolverFunc; + std::string resolvedJson = "resolvedJson"; + MessageRequest unresolvedReq{ + "{}", true, "", std::vector>{}, mockResolverFunc.AsStdFunction()}; + EXPECT_CALL(mockResolverFunc, Call(_, _)) + .WillOnce(Invoke([&](const std::shared_ptr& req, const std::string& resolveKey) { + req->setJsonContent(resolvedJson); + return true; + })); + auto resolvedReq = unresolvedReq.resolveRequest(""); + EXPECT_TRUE(resolvedReq->isResolved()); + EXPECT_TRUE(resolvedReq != nullptr); + EXPECT_EQ(resolvedJson, resolvedReq->getJsonContent()); +} + } // namespace test } // namespace avs } // namespace avsCommon diff --git a/AVSCommon/CMakeLists.txt b/AVSCommon/CMakeLists.txt index e69d7bce79..993c0a18e2 100644 --- a/AVSCommon/CMakeLists.txt +++ b/AVSCommon/CMakeLists.txt @@ -38,6 +38,7 @@ add_library(AVSCommon SHARED AVS/src/HandlerAndPolicy.cpp AVS/src/MessageRequest.cpp AVS/src/NamespaceAndName.cpp + AVS/src/EditableMessageRequest.cpp AVS/src/WaitableMessageRequest.cpp Utils/src/Bluetooth/SDPRecords.cpp Utils/src/BluetoothEventBus.cpp @@ -102,7 +103,6 @@ add_library(AVSCommon SHARED Utils/src/Stream/StreamFunctions.cpp Utils/src/Stream/Streambuf.cpp Utils/src/StringUtils.cpp - Utils/src/SystemClockMonitor.cpp Utils/src/TaskThread.cpp Utils/src/ThreadPool.cpp Utils/src/Threading/ConditionVariableWrapper.cpp diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CallManagerInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CallManagerInterface.h index 743c0e10f4..6e47df1ba0 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CallManagerInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CallManagerInterface.h @@ -115,6 +115,16 @@ class CallManagerInterface */ virtual void unmuteSelf() = 0; + /** + * Enable the video of local device in an active call. + */ + virtual void enableVideo(); + + /** + * Disable the video of local device in an active call. + */ + virtual void disableVideo(); + /** * Check if the call is muted. * @@ -131,6 +141,14 @@ inline CallManagerInterface::CallManagerInterface( avsCommon::avs::CapabilityAgent{avsNamespace, exceptionEncounteredSender} { } +inline void CallManagerInterface::enableVideo() { + return; +} + +inline void CallManagerInterface::disableVideo() { + return; +} + } // namespace sdkInterfaces } // namespace avsCommon } // namespace alexaClientSDK diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CallStateObserverInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CallStateObserverInterface.h index db20c72ebe..bea7e4e067 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CallStateObserverInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CallStateObserverInterface.h @@ -41,6 +41,32 @@ class CallStateObserverInterface { NONE }; + /// An struct containing call state information + typedef struct CallStateInfo { + /// An enum representing the state of a call. + CallState callState; + /// The type of the call. + std::string callType; + /// Previous sipUserAgent state. + std::string previousSipUserAgentState; + /// Current sipUserAgent state. + std::string currentSipUserAgentState; + /// Contact name to be displayed. + std::string displayName; + /// Information about the endpoint of the contact. + std::string endpointLabel; + /// Name of callee for whom incoming call is intended. + std::string inboundCalleeName; + /// Textual description of exact call provider type. + std::string callProviderType; + /// Inbound ringtone url. + std::string inboundRingtoneUrl; + /// Outbound ringtone url. + std::string outboundRingbackUrl; + /// This indicates if it's a drop in call or not + bool isDropIn; + } CallStateInfo; + /** * Destructor */ @@ -54,7 +80,15 @@ class CallStateObserverInterface { virtual void onCallStateChange(CallState state) = 0; /** - * Checks the state of the provided call state to determine if a call is in an "active" state + * Allows the observer to react to a change in call state info. + * + * @param stateInfo The new CallStateInfo. + */ + virtual void onCallStateInfoChange(const CallStateInfo& stateInfo); + + /** + * Checks the state of the provided call state to determine if a call is in + * an "active" state * Active states are: CONNECTING * INBOUND_RINGING * CALL_CONNETED @@ -106,6 +140,11 @@ inline bool CallStateObserverInterface::isStateActive(const CallStateObserverInt return false; } +inline void CallStateObserverInterface::onCallStateInfoChange( + const CallStateObserverInterface::CallStateInfo& stateInfo) { + return; +} + } // namespace sdkInterfaces } // namespace avsCommon } // namespace alexaClientSDK diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilitiesDelegateInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilitiesDelegateInterface.h index daaa0d6540..88593eabdb 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilitiesDelegateInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilitiesDelegateInterface.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include @@ -57,7 +57,7 @@ class CapabilitiesDelegateInterface * if the capabilities are empty; if the attributes or capability configurations are invalid; * if the endpoint is already pending deletion or registration. If this function returns true, * the endpoint will be published to AVS. Callers can be notified of published endpoints using - * @c CapabilitiesObserverInterface. + * @c CapabilitiesDelegateObserverInterface. */ virtual bool addOrUpdateEndpoint( const avsCommon::avs::AVSDiscoveryEndpointAttributes& endpointAttributes, @@ -74,7 +74,7 @@ class CapabilitiesDelegateInterface * if the endpoint is not registered; if the capabilities are empty; if the attributes or capability * configurations are invalid; if the endpoint is already pending deletion or registration. If this * function returns true, the endpoint will be deregistered from AVS. Callers can be notified of - * deregistered endpoints using @c CapabilitiesObserverInterface. + * deregistered endpoints using @c CapabilitiesDelegateObserverInterface. */ virtual bool deleteEndpoint( const avsCommon::avs::AVSDiscoveryEndpointAttributes& endpointAttributes, @@ -88,7 +88,7 @@ class CapabilitiesDelegateInterface * @param observer The object to observe the state of this CapabilitiesDelegate. */ virtual void addCapabilitiesObserver( - std::shared_ptr observer) = 0; + std::shared_ptr observer) = 0; /** * Remove an observer. @@ -96,7 +96,7 @@ class CapabilitiesDelegateInterface * @param observer The observer to remove. */ virtual void removeCapabilitiesObserver( - std::shared_ptr observer) = 0; + std::shared_ptr observer) = 0; /** * Invalidates the capabilities reported to the AVS last. Capabilities information should be rebuilt and reported diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilitiesDelegateObserverInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilitiesDelegateObserverInterface.h new file mode 100644 index 0000000000..cd5728bd48 --- /dev/null +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilitiesDelegateObserverInterface.h @@ -0,0 +1,136 @@ +/* + * 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_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_CAPABILITIESDELEGATEOBSERVERINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_CAPABILITIESDELEGATEOBSERVERINTERFACE_H_ + +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace sdkInterfaces { + +/** + * This interface is used to observe changes to the state of the CapabilitiesDelegate. + */ +class CapabilitiesDelegateObserverInterface { +public: + /// The enum State describes the state of the CapabilitiesDelegate. + enum class State { + /// CapabilitiesDelegate is uninitialized. + UNINITIALIZED, + /// The Capabilities API message went through without issues. + SUCCESS, + /// The message did not go through because of issues that need fixing. + FATAL_ERROR, + /// The message did not go through, but you can retry to see if you succeed. + RETRIABLE_ERROR, + }; + + /// The enum Error encodes possible errors which may occur when changing state. + enum class Error { + /// The state (and hence the error) has not been initialized. + UNINITIALIZED, + /// Success. + SUCCESS, + /// An unknown error occurred. + UNKNOWN_ERROR, + /// The request was canceled. + CANCELED, + /// The authorization failed. + FORBIDDEN, + /// The server encountered a runtime error. + SERVER_INTERNAL_ERROR, + /// The request is missing a required parameter, has an invalid value, or is otherwise improperly formed. + BAD_REQUEST + }; + + /** + * Virtual destructor to assure proper cleanup of derived types. + */ + virtual ~CapabilitiesDelegateObserverInterface() = default; + + /** + * Notification that an CapabilitiesDelegate state has changed. + * + * @note Implementations of this method must not call CapabilitiesDelegate methods because the CapabilitiesDelegate + * may be in a 'locked' state at the time this call is made. If you do, then you may end up with a deadlock. + * + * @param newState The new state of the CapabilitiesDelegate. + * @param newError The error associated to the state change. + * @param addedOrUpdatedEndpointIds The endpoint identifiers of endpoints sent in the addOrUpdateReport. + * @param deletedEndpointIds The endpoint identifiers of endpoints sent in the deleteReport. + */ + virtual void onCapabilitiesStateChange( + State newState, + Error newError, + const std::vector& addedOrUpdatedEndpointIds, + const std::vector& deletedEndpointIds) = 0; +}; + +/** + * Write a @c State value to an @c ostream as a string. + * + * @param stream The stream to write the value to. + * @param state The state value to write to the @c ostream as a string. + * @return The @c ostream that was passed in and written to. + */ +inline std::ostream& operator<<(std::ostream& stream, const CapabilitiesDelegateObserverInterface::State& state) { + switch (state) { + case CapabilitiesDelegateObserverInterface::State::UNINITIALIZED: + return stream << "UNINITIALIZED"; + case CapabilitiesDelegateObserverInterface::State::SUCCESS: + return stream << "SUCCESS"; + case CapabilitiesDelegateObserverInterface::State::FATAL_ERROR: + return stream << "FATAL_ERROR"; + case CapabilitiesDelegateObserverInterface::State::RETRIABLE_ERROR: + return stream << "RETRIABLE_ERROR"; + } + return stream << "Unknown CapabilitiesDelegateObserverInterface::State!: " << state; +} + +/** + * Write an @c Error value to an @c ostream as a string. + * + * @param stream The stream to write the value to. + * @param error The error value to write to the @c ostream as a string. + * @return The @c ostream that was passed in and written to. + */ +inline std::ostream& operator<<(std::ostream& stream, const CapabilitiesDelegateObserverInterface::Error& error) { + switch (error) { + case CapabilitiesDelegateObserverInterface::Error::UNINITIALIZED: + return stream << "UNINITIALIZED"; + case CapabilitiesDelegateObserverInterface::Error::SUCCESS: + return stream << "SUCCESS"; + case CapabilitiesDelegateObserverInterface::Error::UNKNOWN_ERROR: + return stream << "UNKNOWN_ERROR"; + case CapabilitiesDelegateObserverInterface::Error::FORBIDDEN: + return stream << "FORBIDDEN"; + case CapabilitiesDelegateObserverInterface::Error::SERVER_INTERNAL_ERROR: + return stream << "SERVER_INTERNAL_ERROR"; + case CapabilitiesDelegateObserverInterface::Error::BAD_REQUEST: + return stream << "CLIENT_ERROR_BAD_REQUEST"; + case CapabilitiesDelegateObserverInterface::Error::CANCELED: + return stream << "CANCELED"; + } + return stream << "Unknown CapabilitiesDelegateObserverInterface::Error!: " << error; +} + +} // namespace sdkInterfaces +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_CAPABILITIESDELEGATEOBSERVERINTERFACE_H_ diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilitiesObserverInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilitiesObserverInterface.h index 297401ea12..05a1ba7a1f 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilitiesObserverInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilitiesObserverInterface.h @@ -16,118 +16,19 @@ #ifndef ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_CAPABILITIESOBSERVERINTERFACE_H_ #define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_CAPABILITIESOBSERVERINTERFACE_H_ -#include -#include +#include namespace alexaClientSDK { namespace avsCommon { namespace sdkInterfaces { /** - * This interface is used to observe changes to the state of the CapabilitiesDelegate. - */ -class CapabilitiesObserverInterface { -public: - /// The enum State describes the state of the CapabilitiesDelegate. - enum class State { - /// CapabilitiesDelegate is uninitialized. - UNINITIALIZED, - /// The Capabilities API message went through without issues. - SUCCESS, - /// The message did not go through because of issues that need fixing. - FATAL_ERROR, - /// The message did not go through, but you can retry to see if you succeed. - RETRIABLE_ERROR, - }; - - /// The enum Error encodes possible errors which may occur when changing state. - enum class Error { - /// The state (and hence the error) has not been initialized. - UNINITIALIZED, - /// Success. - SUCCESS, - /// An unknown error occurred. - UNKNOWN_ERROR, - /// The request was canceled. - CANCELED, - /// The authorization failed. - FORBIDDEN, - /// The server encountered a runtime error. - SERVER_INTERNAL_ERROR, - /// The request is missing a required parameter, has an invalid value, or is otherwise improperly formed. - BAD_REQUEST - }; - - /** - * Virtual destructor to assure proper cleanup of derived types. - */ - virtual ~CapabilitiesObserverInterface() = default; - - /** - * Notification that an CapabilitiesDelegate state has changed. - * - * @note Implementations of this method must not call CapabilitiesDelegate methods because the CapabilitiesDelegate - * may be in a 'locked' state at the time this call is made. If you do, then you may end up with a deadlock. - * - * @param newState The new state of the CapabilitiesDelegate. - * @param newError The error associated to the state change. - * @param addedOrUpdatedEndpointIds The endpoint identifiers of endpoints sent in the addOrUpdateReport. - * @param deletedEndpointIds The endpoint identifiers of endpoints sent in the deleteReport. - */ - virtual void onCapabilitiesStateChange( - State newState, - Error newError, - const std::vector& addedOrUpdatedEndpointIds, - const std::vector& deletedEndpointIds) = 0; -}; - -/** - * Write a @c State value to an @c ostream as a string. - * - * @param stream The stream to write the value to. - * @param state The state value to write to the @c ostream as a string. - * @return The @c ostream that was passed in and written to. - */ -inline std::ostream& operator<<(std::ostream& stream, const CapabilitiesObserverInterface::State& state) { - switch (state) { - case CapabilitiesObserverInterface::State::UNINITIALIZED: - return stream << "UNINITIALIZED"; - case CapabilitiesObserverInterface::State::SUCCESS: - return stream << "SUCCESS"; - case CapabilitiesObserverInterface::State::FATAL_ERROR: - return stream << "FATAL_ERROR"; - case CapabilitiesObserverInterface::State::RETRIABLE_ERROR: - return stream << "RETRIABLE_ERROR"; - } - return stream << "Unknown CapabilitiesObserverInterface::State!: " << state; -} - -/** - * Write an @c Error value to an @c ostream as a string. + * @deprecated Use @c CapabilitiesDelegateObserverInterface instead. This interface has been + * renamed to avoid being confused with the @c CapabilityConfigurationChangeObserverInterface. * - * @param stream The stream to write the value to. - * @param error The error value to write to the @c ostream as a string. - * @return The @c ostream that was passed in and written to. + * This alias is being maintained for now to support backwards compatibility. */ -inline std::ostream& operator<<(std::ostream& stream, const CapabilitiesObserverInterface::Error& error) { - switch (error) { - case CapabilitiesObserverInterface::Error::UNINITIALIZED: - return stream << "UNINITIALIZED"; - case CapabilitiesObserverInterface::Error::SUCCESS: - return stream << "SUCCESS"; - case CapabilitiesObserverInterface::Error::UNKNOWN_ERROR: - return stream << "UNKNOWN_ERROR"; - case CapabilitiesObserverInterface::Error::FORBIDDEN: - return stream << "FORBIDDEN"; - case CapabilitiesObserverInterface::Error::SERVER_INTERNAL_ERROR: - return stream << "SERVER_INTERNAL_ERROR"; - case CapabilitiesObserverInterface::Error::BAD_REQUEST: - return stream << "CLIENT_ERROR_BAD_REQUEST"; - case CapabilitiesObserverInterface::Error::CANCELED: - return stream << "CANCELED"; - } - return stream << "Unknown CapabilitiesObserverInterface::Error!: " << error; -} +using CapabilitiesObserverInterface = CapabilitiesDelegateObserverInterface; } // namespace sdkInterfaces } // namespace avsCommon diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilityConfigurationChangeObserverInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilityConfigurationChangeObserverInterface.h new file mode 100644 index 0000000000..ed6757c649 --- /dev/null +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/CapabilityConfigurationChangeObserverInterface.h @@ -0,0 +1,49 @@ +/* + * 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_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_CAPABILITYCONFIGURATIONCHANGEOBSERVERINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_CAPABILITYCONFIGURATIONCHANGEOBSERVERINTERFACE_H_ + +#include + +#include "AVSCommon/AVS/CapabilityConfiguration.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace sdkInterfaces { + +/** + * This interface is used to observe CapabilityAgent capability configuration change. + */ +class CapabilityConfigurationChangeObserverInterface { +public: + /** + * Virtual destructor to assure proper cleanup of derived types. + */ + virtual ~CapabilityConfigurationChangeObserverInterface() = default; + + /** + * Notification that a CapabilityAgent configuration is changed. + * + * @param configuration The CapabilityAgent configuration. + */ + virtual void onConfigurationChanged(const avs::CapabilityConfiguration& configuration) = 0; +}; + +} // namespace sdkInterfaces +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_CAPABILITYCONFIGURATIONCHANGEOBSERVERINTERFACE_H_ \ No newline at end of file diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ChannelVolumeInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ChannelVolumeInterface.h index b6cdb972fb..bb0e7c5962 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ChannelVolumeInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ChannelVolumeInterface.h @@ -157,13 +157,13 @@ inline bool ChannelVolumeInterface::adjustUnduckedVolume(int8_t delta) { if (!getSpeakerSettings(&settings)) { return false; } - int8_t volume = settings.volume + delta; + int volume = static_cast(settings.volume) + static_cast(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); + return setUnduckedVolume(static_cast(volume)); } } // namespace sdkInterfaces diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/LocaleAssetsManagerInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/LocaleAssetsManagerInterface.h index c231a0bc00..18b1536da1 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/LocaleAssetsManagerInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/LocaleAssetsManagerInterface.h @@ -21,6 +21,10 @@ #include #include +#include +#include +#include + namespace alexaClientSDK { namespace avsCommon { namespace sdkInterfaces { @@ -33,7 +37,7 @@ namespace sdkInterfaces { * * The methods @c prepareAssets and @c cancelPrepare MUST be thread safe. */ -class LocaleAssetsManagerInterface { +class LocaleAssetsManagerInterface : public CapabilityConfigurationChangeObserverInterface { public: /** * Alias for the locale. The locale should follow BCP 47 format and @@ -144,6 +148,28 @@ class LocaleAssetsManagerInterface { */ virtual WakeWordsSets getSupportedWakeWords(const Locale& locale) const = 0; + /** + * Add a locale assets observer to be notified when locale assets have updated. + * + * @param observer The observer to add. + */ + virtual void addLocaleAssetsObserver(const std::shared_ptr& observer) = 0; + + /** + * Remove a previously registered observer. + * + * @param observer The observer to be removed. + */ + virtual void removeLocaleAssetsObserver(const std::shared_ptr& observer) = 0; + + /** + * Set the @c EndpointRegistrationManager to update locales/wakewords capabilities. + * + * @param manager The pointer to @c EndpointRegistrationManager. + */ + virtual void setEndpointRegistrationManager( + const std::shared_ptr& manager) = 0; + /** * Destructor. */ diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/LocaleAssetsObserverInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/LocaleAssetsObserverInterface.h new file mode 100644 index 0000000000..ebbe380e34 --- /dev/null +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/LocaleAssetsObserverInterface.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 ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_LOCALEASSETSOBSERVERINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_LOCALEASSETSOBSERVERINTERFACE_H_ + +namespace alexaClientSDK { +namespace avsCommon { +namespace sdkInterfaces { + +/** + * This interface is used to observer locale assets change. + */ +class LocaleAssetsObserverInterface { +public: + /** + * Virtual destructor to assure proper cleanup of derived types. + */ + virtual ~LocaleAssetsObserverInterface() = default; + + /** + * Used to notify the observer of locale assets(wakewords, locales) update. + */ + virtual void onLocaleAssetsChanged() = 0; +}; + +} // namespace sdkInterfaces +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_LOCALEASSETSOBSERVERINTERFACE_H_ \ No newline at end of file diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/MessageRequestObserverInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/MessageRequestObserverInterface.h index af49c87637..3dccb1d11a 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/MessageRequestObserverInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/MessageRequestObserverInterface.h @@ -85,8 +85,17 @@ class MessageRequestObserverInterface { */ virtual ~MessageRequestObserverInterface() = default; + /** + * Called when the Response code is received. + * + * @param status The status of the response that was received. + */ + virtual void onResponseStatusReceived(MessageRequestObserverInterface::Status status){}; + /* * Called when a message request has been processed by AVS. + * + * @param status The status of the response that was received. */ virtual void onSendCompleted(MessageRequestObserverInterface::Status status) = 0; diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeakerManagerObserverInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeakerManagerObserverInterface.h index d91c7fae2e..db58e3dab1 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeakerManagerObserverInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeakerManagerObserverInterface.h @@ -36,7 +36,9 @@ class SpeakerManagerObserverInterface { // The call occurred as a result of an AVS Directive. DIRECTIVE, // The call occurred as a result of a local API call. - LOCAL_API + LOCAL_API, + // The call occurred from an external client + EXTERNAL_CLIENT }; /** @@ -72,6 +74,9 @@ inline std::ostream& operator<<(std::ostream& stream, SpeakerManagerObserverInte case SpeakerManagerObserverInterface::Source::LOCAL_API: stream << "LOCAL_API"; return stream; + case SpeakerManagerObserverInterface::Source::EXTERNAL_CLIENT: + stream << "EXTERNAL_CLIENT"; + return stream; } stream << "UNKNOWN"; return stream; diff --git a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockCapabilitiesDelegate.h b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockCapabilitiesDelegate.h index 91e217b723..f62edbeee5 100644 --- a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockCapabilitiesDelegate.h +++ b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockCapabilitiesDelegate.h @@ -40,10 +40,10 @@ class MockCapabilitiesDelegate : public CapabilitiesDelegateInterface { const std::vector& capabilities)); MOCK_METHOD1( addCapabilitiesObserver, - void(std::shared_ptr observer)); + void(std::shared_ptr observer)); MOCK_METHOD1( removeCapabilitiesObserver, - void(std::shared_ptr observer)); + void(std::shared_ptr observer)); MOCK_METHOD0(invalidateCapabilities, void()); MOCK_METHOD1( setMessageSender, diff --git a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockLocaleAssetsManager.h b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockLocaleAssetsManager.h index 511f197051..184a6a0496 100644 --- a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockLocaleAssetsManager.h +++ b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockLocaleAssetsManager.h @@ -32,6 +32,12 @@ class MockLocaleAssetsManager : public LocaleAssetsManagerInterface { public: MOCK_METHOD2(changeAssets, bool(const Locales& locale, const WakeWords& wakeWords)); MOCK_METHOD0(cancelOngoingChange, void()); + MOCK_METHOD1(addLocaleAssetsObserver, void(const std::shared_ptr& observer)); + MOCK_METHOD1(removeLocaleAssetsObserver, void(const std::shared_ptr& observer)); + MOCK_METHOD1(onConfigurationChanged, void(const avs::CapabilityConfiguration& configuration)); + MOCK_METHOD1( + setEndpointRegistrationManager, + void(const std::shared_ptr& manager)); MOCK_CONST_METHOD0(getDefaultSupportedWakeWords, WakeWordsSets()); MOCK_CONST_METHOD0(getLanguageSpecificWakeWords, std::map()); MOCK_CONST_METHOD0(getLocaleSpecificWakeWords, std::map()); diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Bluetooth/BluetoothEventBus.h b/AVSCommon/Utils/include/AVSCommon/Utils/Bluetooth/BluetoothEventBus.h index 99721b670a..c7280c9698 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Bluetooth/BluetoothEventBus.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Bluetooth/BluetoothEventBus.h @@ -36,6 +36,12 @@ namespace bluetooth { */ class BluetoothEventBus { public: + /** + * Factory method to create a shared pointer to @c BluetoothEventBus. + * @return A new instance of @c BluetoothEventBus. + */ + static std::shared_ptr createBluetoothEventBus(); + /** * Constructor */ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/HTTP2/HTTP2ResponseFinishedStatus.h b/AVSCommon/Utils/include/AVSCommon/Utils/HTTP2/HTTP2ResponseFinishedStatus.h index 0b7e8ce5ba..14a144726f 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/HTTP2/HTTP2ResponseFinishedStatus.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/HTTP2/HTTP2ResponseFinishedStatus.h @@ -55,7 +55,7 @@ inline std::ostream& operator<<(std::ostream& stream, HTTP2ResponseFinishedStatu case HTTP2ResponseFinishedStatus::INTERNAL_ERROR: return stream << "INTERNAL_ERROR"; } - return stream << ""; + return stream << "Invalid(" << static_cast(status) << ")"; } } // namespace http2 diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Logger/LogEntry.h b/AVSCommon/Utils/include/AVSCommon/Utils/Logger/LogEntry.h index 28d0799992..f77920af2a 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Logger/LogEntry.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Logger/LogEntry.h @@ -132,9 +132,9 @@ class LogEntry { /** * Add data in the form of a @c key, @c value pair to the metadata of this log entry. - * If the value includes a privacy blacklist entry, the portion after that will be obfuscated. + * If the value includes a privacy denylist entry, the portion after that will be obfuscated. * This is done in a distinct method (instead of m or d) to avoid the cost of always checking - * against the blacklist. + * against the denylist. * * @param key The key identifying the value to add to this LogEntry. * @param value The value to add to this LogEntry, obfuscated if needed. @@ -197,9 +197,9 @@ class LogEntry { void prefixMessage(); /// Return a list of labels we will obfuscate if sent to obfuscatePrivateData - static std::vector getPrivateLabelBlacklist() { - static std::vector privateLabelBlacklist = {"ssid"}; - return privateLabelBlacklist; + static std::vector getPrivateLabelDenyList() { + static std::vector privateLabelDenyList = {"ssid"}; + return privateLabelDenyList; } /** @@ -253,13 +253,13 @@ LogEntry& LogEntry::obfuscatePrivateData(const char* key, const std::string& val // since it can (but shouldn't) contain multiple, obfuscate from the earliest one found onward auto firstPosition = value.length(); - for (auto privateLabel : getPrivateLabelBlacklist()) { + for (auto privateLabel : getPrivateLabelDenyList()) { auto it = std::search( value.begin(), value.end(), privateLabel.begin(), privateLabel.end(), - [](char valueChar, char blackListChar) { return std::tolower(valueChar) == std::tolower(blackListChar); }); + [](char valueChar, char denyListChar) { return std::tolower(valueChar) == std::tolower(denyListChar); }); if (it != value.end()) { // capture the least value auto thisPosition = std::distance(value.begin(), it) + privateLabel.length(); diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerInterface.h b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerInterface.h index 6b016951fe..8e0032742c 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerInterface.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerInterface.h @@ -96,6 +96,28 @@ class MediaPlayerInterface { const avsCommon::utils::AudioFormat* format = nullptr, const SourceConfig& config = emptySourceConfig()) = 0; + /** + * Set an @c AttachmentReader source to play. The source should be set before making calls to any of the playback + * control APIs. If any source was set prior to this call, that source will be discarded. + * + * @note A @c MediaPlayerInterface implementation must handle only one source at a time. An implementation must call + * @c MediaPlayerObserverInterface::onPlaybackStopped() with the previous source's id if there was a source set. + * Also, an implementation must call @c MediaPlayerObserverInterface::onBufferingComplete() when this source has + * been fully buffered + * + * @param attachmentReader Object with which to read an incoming audio attachment. + * @param format The audioFormat to be used to interpret raw audio data. + * @param offsetAdjustment Offset adjustment required for the offset reported + * @param config Media configuration of source. + * @return The @c SourceId that represents the source being handled as a result of this call. @c ERROR will be + * returned if the source failed to be set. Must be unique across all instances. + */ + virtual SourceId setSource( + std::shared_ptr attachmentReader, + std::chrono::milliseconds offsetAdjustment, + const avsCommon::utils::AudioFormat* format = nullptr, + const SourceConfig& config = emptySourceConfig()) = 0; + /** * Set a url source to play. The source should be set before making calls to any of the playback control APIs. If * any source was set prior to this call, that source will be discarded. diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/SDKVersion.h b/AVSCommon/Utils/include/AVSCommon/Utils/SDKVersion.h index d909264107..4266fa9bf8 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.22.0"; + return "1.23.0"; } inline static int getMajorVersion() { @@ -37,7 +37,7 @@ inline static int getMajorVersion() { } inline static int getMinorVersion() { - return 22; + return 23; } inline static int getPatchVersion() { diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/SDS/SharedDataStream.h b/AVSCommon/Utils/include/AVSCommon/Utils/SDS/SharedDataStream.h index b08a66e59c..4773427342 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/SDS/SharedDataStream.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/SDS/SharedDataStream.h @@ -485,8 +485,13 @@ std::unique_ptr::Reader> SharedDataStream::creat for (int i = 0; i < MAX_READER_CREATION_RETRIES; i++) { auto offset = m_bufferLayout->getDataSize(); auto writeStartCursor = headerPtr->writeStartCursor.load(); + auto reference = Reader::Reference::BEFORE_WRITER; if (writeStartCursor < offset) { - offset = writeStartCursor; + // For SDS without buffer overwritten, seek the very beginning of the stream using ABSOLUTE reference + // ABSOLUTE reference prevents a race condition between blocking writer and reader seek when writer + // writes more data to buffer, and writing cursor moves forward. + offset = 0; + reference = Reader::Reference::ABSOLUTE; } else { auto writeEndCursor = headerPtr->writeEndCursor.load(); if (writeEndCursor < writeStartCursor) { @@ -505,7 +510,7 @@ std::unique_ptr::Reader> SharedDataStream::creat offset -= wordsBeingWritten; } - if (reader->seek(offset, Reader::Reference::BEFORE_WRITER)) { + if (reader->seek(offset, reference)) { // Note: seek() will call updateUnconsumedCursor() if it returns true. return reader; } diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/MultiTimer.h b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/MultiTimer.h index 22dd92348d..169a80b57b 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/MultiTimer.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/MultiTimer.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "AVSCommon/Utils/Threading/Executor.h" @@ -40,6 +41,12 @@ class MultiTimer { /// Alias for the token used to identify a task. This can be used to cancel a task execution. using Token = uint64_t; + /** + * Factory method that creates a shared pointer to a MultiTimer. + * @return A new instance of MultiTimer. + */ + static std::shared_ptr createMultiTimer(); + /** * Constructor. */ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/SystemClockMonitor.h b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/SystemClockMonitor.h deleted file mode 100644 index 53ec8c62e0..0000000000 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/SystemClockMonitor.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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_TIMING_SYSTEMCLOCKMONITOR_H_ -#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_TIMING_SYSTEMCLOCKMONITOR_H_ - -#include -#include - -#include - -namespace alexaClientSDK { -namespace avsCommon { -namespace utils { -namespace timing { - -/** - * This class monitors the system clock. When the system clock is synchronized, this class notifies - * its observers of the synchronization. - */ -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) - */ - void notifySystemClockSynchronized(); - - /** - * Add an observer to the system clock monitor - * @param observer The observer to add to our set - */ - void addSystemClockMonitorObserver( - const std::shared_ptr& observer); - - /** - * Remove an observer to the system clock monitor - * @param observer The observer to remove from our set - */ - void removeSystemClockMonitorObserver( - const std::shared_ptr& observer); - -private: - /** - * Notify observers that device clock has synchronized - */ - void notifyObservers(); - - /// Mutex for system clock observers. - std::mutex m_systemClockObserverMutex; - - // The set of SystemClockMonitor observers - std::unordered_set> m_observers; -}; - -} // namespace timing -} // namespace utils -} // namespace avsCommon -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_TIMING_SYSTEMCLOCKMONITOR_H_ diff --git a/AVSCommon/Utils/src/BluetoothEventBus.cpp b/AVSCommon/Utils/src/BluetoothEventBus.cpp index 83d8beeb22..3753a2e744 100644 --- a/AVSCommon/Utils/src/BluetoothEventBus.cpp +++ b/AVSCommon/Utils/src/BluetoothEventBus.cpp @@ -32,6 +32,10 @@ static const std::string TAG{"BluetoothEventBus"}; */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) +std::shared_ptr BluetoothEventBus::createBluetoothEventBus() { + return std::make_shared(); +} + BluetoothEventBus::BluetoothEventBus() { } diff --git a/AVSCommon/Utils/src/HTTP2/HTTP2MimeResponseDecoder.cpp b/AVSCommon/Utils/src/HTTP2/HTTP2MimeResponseDecoder.cpp index 773f183378..a6598cb021 100644 --- a/AVSCommon/Utils/src/HTTP2/HTTP2MimeResponseDecoder.cpp +++ b/AVSCommon/Utils/src/HTTP2/HTTP2MimeResponseDecoder.cpp @@ -14,6 +14,7 @@ */ #include +#include #include "AVSCommon/Utils/HTTP/HttpResponseCode.h" #include "AVSCommon/Utils/HTTP2/HTTP2MimeResponseDecoder.h" @@ -37,19 +38,21 @@ static const std::string TAG("HTTP2MimeResponseDecoder"); /// ASCII value of CR static const char CARRIAGE_RETURN_ASCII = 13; +/// ASCII value of Double Quotes (") +static const char QUOTES_CHAR = '\"'; /// ASCII value of LF static const char LINE_FEED_ASCII = 10; /// CRLF sequence static const char CRLF_SEQUENCE[] = {CARRIAGE_RETURN_ASCII, LINE_FEED_ASCII}; /// Size of CLRF in chars static const int LEADING_CRLF_CHAR_SIZE = sizeof(CRLF_SEQUENCE) / sizeof(*CRLF_SEQUENCE); - /// MIME boundary string prefix in HTTP header. static const std::string BOUNDARY_PREFIX = "boundary="; -/// Size in chars of the MIME boundary string prefix -static const int BOUNDARY_PREFIX_SIZE = BOUNDARY_PREFIX.size(); -/// MIME HTTP header value delimiter -static const std::string BOUNDARY_DELIMITER = ";"; +/// Non alpha-numeric chars that are allowed as part of the Boundary as per RFC2046 +static const std::unordered_set BOUNDARY_ALLOWED_NON_ALPHA_CHARS = + {'\'', '(', ')', '+', '_', ',', '-', '.', '/', ':', '=', '?'}; +/// Max length of the boundary as per RFC2046 (inclusive) +static const size_t BOUNDARY_MAX_LENGTH = 70; HTTP2MimeResponseDecoder::HTTP2MimeResponseDecoder(std::shared_ptr sink) : m_sink{sink}, @@ -87,11 +90,33 @@ bool HTTP2MimeResponseDecoder::onReceiveHeaderLine(const std::string& line) { } if (!m_boundaryFound) { - if (line.find(BOUNDARY_PREFIX) != std::string::npos) { - std::string boundary{line.substr(line.find(BOUNDARY_PREFIX))}; - boundary = boundary.substr(BOUNDARY_PREFIX_SIZE, boundary.find(BOUNDARY_DELIMITER) - BOUNDARY_PREFIX_SIZE); - m_multipartReader.setBoundary(boundary); - m_boundaryFound = true; + size_t boundaryPosition = line.find(BOUNDARY_PREFIX); + if (boundaryPosition != std::string::npos) { + size_t boundaryIndexStart = boundaryPosition + BOUNDARY_PREFIX.size(); + if (line[boundaryIndexStart] == QUOTES_CHAR) { + ++boundaryIndexStart; + } + size_t boundaryIndex = boundaryIndexStart; + for (; boundaryIndex < line.size(); boundaryIndex++) { + char currentChar = line[boundaryIndex]; + // if the char is not an alpha-numeric of not in the allowed chars, stop + // this case also handles the closing quotes (discarding everything after the second ") + if ((!isalnum(currentChar) && + (BOUNDARY_ALLOWED_NON_ALPHA_CHARS.find(currentChar) == BOUNDARY_ALLOWED_NON_ALPHA_CHARS.end()))) { + break; + } + } + std::string boundary = line.substr(boundaryIndexStart, boundaryIndex - boundaryIndexStart); + // as per NFC2046 the boundary should range from 1 to 70 chars + if (boundary.size() > 0 && boundary.size() <= BOUNDARY_MAX_LENGTH) { + m_multipartReader.setBoundary(boundary); + m_boundaryFound = true; + ACSDK_DEBUG9(LX(__func__).d("boundary", boundary)); + } else { + // this happens when a we find a 'boundary=X' where X is invalid as per RFC + ACSDK_ERROR(LX("invalidBoundary")); + return false; + } } } diff --git a/AVSCommon/Utils/src/JSON/JSONUtils.cpp b/AVSCommon/Utils/src/JSON/JSONUtils.cpp index 65c039e496..3cf8edb1d0 100644 --- a/AVSCommon/Utils/src/JSON/JSONUtils.cpp +++ b/AVSCommon/Utils/src/JSON/JSONUtils.cpp @@ -249,12 +249,12 @@ bool convertToValue(const rapidjson::Value& documentNode, double* value) { bool jsonArrayExists(const rapidjson::Value& parsedDocument, const std::string& key) { auto iter = parsedDocument.FindMember(key); if (parsedDocument.MemberEnd() == iter) { - ACSDK_ERROR(LX("lookupArrayExistsFailed").d("reason", "keyNotFound").d("key", key)); + ACSDK_DEBUG0(LX(__func__).d("reason", "keyNotFound").d("key", key)); return false; } if (!iter->value.IsArray()) { - ACSDK_ERROR(LX("lookupArrayExistsFailed").d("reason", "notArrayType")); + ACSDK_DEBUG0(LX(__func__).d("reason", "notArrayType").d("key", key).d("type", parsedDocument.GetType())); return false; } diff --git a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Request.cpp b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Request.cpp index d062c37b5a..74d5eef545 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Request.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2Request.cpp @@ -151,12 +151,20 @@ LibcurlHTTP2Request::LibcurlHTTP2Request( m_stream.setTransferType(CurlEasyHandleWrapper::TransferType::kGET); break; case HTTP2RequestType::POST: - curl_easy_setopt(m_stream.getCurlHandle(), CURLOPT_POST, 1L); + CURLcode ret = curl_easy_setopt(m_stream.getCurlHandle(), CURLOPT_POST, 1L); + if (ret != CURLE_OK) { + ACSDK_WARN( + LX("Configuring the request").d("reason", "curlFailure").d("error", curl_easy_strerror(ret))); + } m_stream.setReadCallback(LibcurlHTTP2Request::readCallback, this); break; } - m_stream.setURL(config.getUrl()); - m_stream.setWriteCallback(LibcurlHTTP2Request::writeCallback, this); + if (!m_stream.setURL(config.getUrl())) { + ACSDK_WARN(LX("Configuring the request").d("reason", "setURL failed")); + } + if (!m_stream.setWriteCallback(LibcurlHTTP2Request::writeCallback, this)) { + ACSDK_WARN(LX("Configuring the request").d("reason", "setWriteCallback failed")); + } m_stream.setHeaderCallback(LibcurlHTTP2Request::headerCallback, this); m_stream.curlOptionsSetter().setopt(CURLOPT_TCP_KEEPALIVE, 1); m_stream.curlOptionsSetter().setopt(CURLOPT_STREAM_WEIGHT, config.getPriority()); @@ -168,7 +176,10 @@ LibcurlHTTP2Request::LibcurlHTTP2Request( m_source = config.getSource(); auto headers = m_source->getRequestHeaderLines(); for (const auto& header : headers) { - m_stream.addHTTPHeader(header); + bool result = m_stream.addHTTPHeader(header); + if (!result) { + ACSDK_WARN(LX("addHTTPHeader failed")); + } } } if (config.getSink()) { diff --git a/AVSCommon/Utils/src/MultiTimer.cpp b/AVSCommon/Utils/src/MultiTimer.cpp index 8c47785f45..2ae626e1ea 100644 --- a/AVSCommon/Utils/src/MultiTimer.cpp +++ b/AVSCommon/Utils/src/MultiTimer.cpp @@ -37,6 +37,10 @@ static const std::string TAG("MultiTimer"); /// Grace period used to avoid restarting the internal thread too often. static const std::chrono::milliseconds GRACE_PERIOD{500}; +std::shared_ptr MultiTimer::createMultiTimer() { + return std::make_shared(); +} + MultiTimer::MultiTimer() : m_isRunning{false}, m_isBeingDestroyed{false}, m_nextToken{0} { } diff --git a/AVSCommon/Utils/src/SystemClockMonitor.cpp b/AVSCommon/Utils/src/SystemClockMonitor.cpp deleted file mode 100644 index e0c9c5bdd2..0000000000 --- a/AVSCommon/Utils/src/SystemClockMonitor.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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/Logger/Logger.h" -#include - -/// String to identify log entries originating from this file. -static const std::string TAG("SystemClockMonitor"); - -/** - * 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 timing { - -std::shared_ptr SystemClockMonitor::createSystemClockMonitor() { - return std::make_shared(); -} - -void SystemClockMonitor::notifySystemClockSynchronized() { - notifyObservers(); -} - -void SystemClockMonitor::addSystemClockMonitorObserver( - const std::shared_ptr& observer) { - if (!observer) { - ACSDK_ERROR(LX("addObserverFailed").d("reason", "nullObserver")); - return; - } - - std::lock_guard lock{m_systemClockObserverMutex}; - m_observers.insert(observer); -} - -void SystemClockMonitor::removeSystemClockMonitorObserver( - const std::shared_ptr& observer) { - if (!observer) { - ACSDK_ERROR(LX("removeObserverFailed").d("reason", "nullObserver")); - return; - } - - std::lock_guard lock{m_systemClockObserverMutex}; - m_observers.erase(observer); -} - -void SystemClockMonitor::notifyObservers() { - std::unique_lock lock{m_systemClockObserverMutex}; - auto observersCopy = m_observers; - lock.unlock(); - - for (const auto& observer : observersCopy) { - observer->onSystemClockSynchronized(); - } -} - -} // namespace timing -} // namespace utils -} // namespace avsCommon -} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/src/Threading/ConditionVariableWrapper.cpp b/AVSCommon/Utils/src/Threading/ConditionVariableWrapper.cpp index c78c7fb59a..547e395c0f 100644 --- a/AVSCommon/Utils/src/Threading/ConditionVariableWrapper.cpp +++ b/AVSCommon/Utils/src/Threading/ConditionVariableWrapper.cpp @@ -169,22 +169,46 @@ bool ConditionVariableWrapper::waitForInner( return m_cv.wait_for(lock, relTime, pred); } - bool timedOut = false; timing::Timer timer; - std::mutex& outerMutex = *lock.mutex(); + /* + * The timer task may execute even after the original predicate (pred()) has returned true. + * This is because: + * + * 1. The timer may expire before stop() is called but after wait(...) has returned. + * 2. Calling Timer::stop() does not halt any currently executing task. + * + * Reusing the outer lock (lock) in the timer task will result in deadlock in the above scenarios. + * This is becase wait(...) will have returned, thus lock will be locked. + * + * We use a separate lock to ensure that timedOut is synchronized. + * Using an atomic is insufficient because it's possible that + * timedOut and notifyAll() are called after the predicateOrTimedout logic is evaluated, + * which would cause this notification to be missed. + * + * 1. predicateOrTimedout is called and timedOut is evaluated to be false. Before thread waits, Step 2 happens. + * 2. Timer task fires. + * 3. timedOut is set to true. + * 4. notifyAll() is called. Thread in Step 1 is still running, thus no threads are blocked. + * 5. Timer task exits. + * 6. Thread in step 1 goes to wait, misses notification. + */ + std::mutex timerMutex; + bool timedOut = false; - timer.start(relTime, [this, &timedOut, &outerMutex]() { - ACSDK_DEBUG9(LX("timer").d("reason", "timedOut")); + timer.start(relTime, [this, &timedOut, &timerMutex]() { + ACSDK_DEBUG9(LX("timeoutTask").d("reason", "timerExpired")); + std::lock_guard timerLock(timerMutex); - std::unique_lock timerLock(outerMutex); timedOut = true; - timerLock.unlock(); // Wake up all threads to re-evaluate predicates. notifyAll(); }); - auto predicateOrTimedout = [pred, &timedOut]() { return pred() || timedOut; }; + auto predicateOrTimedout = [pred, &timedOut, &timerMutex]() { + std::lock_guard timerLock(timerMutex); + return pred() || timedOut; + }; wait(lock, predicateOrTimedout); diff --git a/AVSCommon/Utils/test/AVSCommon/Utils/MediaPlayer/MockMediaPlayer.h b/AVSCommon/Utils/test/AVSCommon/Utils/MediaPlayer/MockMediaPlayer.h index d243061928..497c5cf3ad 100644 --- a/AVSCommon/Utils/test/AVSCommon/Utils/MediaPlayer/MockMediaPlayer.h +++ b/AVSCommon/Utils/test/AVSCommon/Utils/MediaPlayer/MockMediaPlayer.h @@ -103,6 +103,11 @@ class MockMediaPlayer std::shared_ptr attachmentReader, const avsCommon::utils::AudioFormat* audioFormat = nullptr, const SourceConfig& config = emptySourceConfig()) /*override*/; + SourceId setSource( + std::shared_ptr attachmentReader, + std::chrono::milliseconds offsetAdjustment, + const avsCommon::utils::AudioFormat* audioFormat = nullptr, + const SourceConfig& config = emptySourceConfig()) /*override*/; SourceId setSource( const std::string& url, std::chrono::milliseconds offset = std::chrono::milliseconds::zero(), diff --git a/AVSCommon/Utils/test/Common/MockMediaPlayer.cpp b/AVSCommon/Utils/test/Common/MockMediaPlayer.cpp index 5a71e16213..533314aba5 100644 --- a/AVSCommon/Utils/test/Common/MockMediaPlayer.cpp +++ b/AVSCommon/Utils/test/Common/MockMediaPlayer.cpp @@ -69,6 +69,14 @@ MediaPlayerInterface::SourceId MockMediaPlayer::setSource( return attachmentSetSource(attachmentReader, audioFormat); } +MediaPlayerInterface::SourceId MockMediaPlayer::setSource( + std::shared_ptr attachmentReader, + std::chrono::milliseconds offsetAdjustment, + const avsCommon::utils::AudioFormat* audioFormat, + const SourceConfig&) { + return attachmentSetSource(attachmentReader, audioFormat); +} + MediaPlayerInterface::SourceId MockMediaPlayer::setSource( const std::string& url, std::chrono::milliseconds, diff --git a/AVSCommon/Utils/test/LoggerTest.cpp b/AVSCommon/Utils/test/LoggerTest.cpp index a71c056054..da6f77c053 100644 --- a/AVSCommon/Utils/test/LoggerTest.cpp +++ b/AVSCommon/Utils/test/LoggerTest.cpp @@ -609,7 +609,7 @@ TEST_F(LoggerTest, test_obfuscatedDataIsMangled) { auto presentInOriginalForm = g_log->m_lastText.find(TEST_MESSAGE_STRING) != std::string::npos; ASSERT_TRUE(presentInOriginalForm); - // a private value should not appear, the blacklist word itself should + // a private value should not appear, the denylist word itself should ACSDK_INFO(LX("testing metadata obfuscation") .obfuscatePrivateData(METADATA_KEY, "{\"ESSID\"\\:\"I_NAME_MY_SSID_AFTER_MY_CREDIT_CARD_NUMBER\"}")); auto lastLineAfterPrivateSubmission = g_log->m_lastText; diff --git a/AVSCommon/Utils/test/MIMEParserTest.cpp b/AVSCommon/Utils/test/MIMEParserTest.cpp index 30691eb9ff..6a61f14af6 100644 --- a/AVSCommon/Utils/test/MIMEParserTest.cpp +++ b/AVSCommon/Utils/test/MIMEParserTest.cpp @@ -71,6 +71,8 @@ static const std::string TEST_MESSAGE = "{\"directive\":{\"header\":{\"namespace\":\"SpeechRecognizer\",\"name\":" "\"StopCapture\",\"messageId\":\"4e5612af-e05c-4611-8910-1e23f47ffb41\"}," "\"payload\":{}}}"; +/// The " char +static const std::string QUOTE_CHAR = "\""; // The following *_LINES definitions are raw mime text for various test parts. Each one assumes that // it will be prefixed by a boundary and a CRLF. These get concatenated by constructTestMimeString() @@ -228,17 +230,17 @@ TEST_F(MIMEParserTest, test_encodingSanity) { * Helper method to run boundary check tests. */ void runDecodingBoundariesTest( - std::vector> partsToIndex, - std::vector> testCaseExpectedHeaders, - std::vector testCaseExpectedData, - std::string boundary, + const std::vector>& partsToIndex, + const std::vector>& testCaseExpectedHeaders, + const std::vector& testCaseExpectedData, + const std::vector& headers, HTTP2ReceiveDataStatus expectedStatus = HTTP2ReceiveDataStatus::SUCCESS) { - const std::string boundaryHeader{BOUNDARY_HEADER_PREFIX + boundary}; auto sink = std::make_shared(); HTTP2MimeResponseDecoder decoder{sink}; HTTP2ReceiveDataStatus status{HTTP2ReceiveDataStatus::SUCCESS}; - bool resp = decoder.onReceiveHeaderLine(boundaryHeader); - ASSERT_TRUE(resp); + for (auto header : headers) { + ASSERT_TRUE(decoder.onReceiveHeaderLine(header)); + } decoder.onReceiveResponseCode(HTTPResponseCode::SUCCESS_OK); // send the data part by part like the cloud does. for (auto part : partsToIndex) { @@ -247,10 +249,156 @@ void runDecodingBoundariesTest( // this assertion is trying to detect if a partEnd call happened, we use m_index to detect that ASSERT_EQ(sink->m_index, part.second); } - ASSERT_EQ(expectedStatus, status); ASSERT_EQ(sink->m_index, partsToIndex.back().second); ASSERT_EQ(sink->m_headers, testCaseExpectedHeaders); ASSERT_EQ(sink->m_data, testCaseExpectedData); + ASSERT_EQ(expectedStatus, status); +} + +/* + * Helper method to run boundary tests with a default payload, and provided headers + */ +void generatePayloadAndTest(const std::vector& headers, const std::string& payloadBoundary) { + std::vector> parts = { + {"--" + payloadBoundary + "\r\ncontent-type: multipart/related\r\n\r\n", 0}, + {"1111", 0}, + {"2222", 0}, + {"3333", 0}, + {"\r\n--" + payloadBoundary, 1}, // the boundary is sent on its own and we want to detect a part end here. + {"\r\ncontent-type: multipart/related\r\n\r\nlast\r\n--" + payloadBoundary, 2}, + {"--\r\n", 2}}; + const std::vector> testCaseExpectedHeaders = { + {{"content-type", "multipart/related"}}, {{"content-type", "multipart/related"}}}; + const std::vector testCaseExpectedData = {"111122223333", "last"}; + runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, headers); +} + +/* + * Helper method to run boundary tests with a default payload, and provided boundary + */ +void generatePayloadAndTest(const std::string& headerBoundary, const std::string& payloadBoundary) { + const std::vector headers = {BOUNDARY_HEADER_PREFIX + headerBoundary}; + generatePayloadAndTest(headers, payloadBoundary); +} + +/* + * Given a partition and a payload, send each part of the payload individually. + * Assert if the parsing is correct and if the content is as expected. + */ +void runTestForCombination(std::vector& partition, const std::string& payload, const std::string& boundary) { + // we can skip a combination that has a empty partition, this means that the same combination will appear elsewhere. + // example numberOfPartitions = 3 => [][a][bcd] is the same as numberOfPartitions = 2 => [a][bcd] + if (std::find(partition.begin(), partition.end(), 0) != partition.end()) { + return; + } + + const std::vector testCaseExpectedData = {"data"}; + const std::string boundaryHeader{BOUNDARY_HEADER_PREFIX + boundary}; + auto sink = std::make_shared(); + HTTP2MimeResponseDecoder decoder{sink}; + HTTP2ReceiveDataStatus status{HTTP2ReceiveDataStatus::SUCCESS}; + bool resp = decoder.onReceiveHeaderLine(boundaryHeader); + ASSERT_TRUE(resp); + decoder.onReceiveResponseCode(HTTPResponseCode::SUCCESS_OK); + + size_t pIndex = 0; + for (size_t i = 0; i < partition.size(); i++) { + // send a part of the payload to the parser + status = decoder.onReceiveData(payload.c_str() + pIndex, partition[i]); + ASSERT_EQ(status, HTTP2ReceiveDataStatus::SUCCESS); + pIndex += partition[i]; + } + // at the end of all parts check if the message contents is as expected. + ASSERT_EQ(sink->m_data, testCaseExpectedData); +} + +void generateCombinationsAndRunTest( + std::vector& partitions, + size_t pos, + size_t remaining, + const std::string& words, + const std::string& boundary) { + if (remaining == 0) { + runTestForCombination(partitions, words, boundary); + return; + } + if (pos == partitions.size()) { + return; + } + for (int i = remaining; i >= 0; --i) { + partitions[pos] = i; + generateCombinationsAndRunTest(partitions, pos + 1, remaining - i, words, boundary); + } +} + +/* + * This test will split the payload into groups [abcd] of a given size k. + * We then vary k to have all possible group sizes. + * numberOfPartitions = 1 => [abcd] + * numberOfPartitions = 2 => [abc,d], [ab,cd], [a,bcd] + * until numberOfPartitions = payload.size (one char at a time) + */ +TEST_F(MIMEParserTest, test_multipleCombinations) { + std::string payload = + "--WWWoooAAA\r\nContent-Type: " + "application/json\r\n\r\ndata\r\n--WWWoooAAA--\r\n"; + size_t numberOfPartitionsIncrement = 1; + // this test can be really slow, so the maxNumberOfPartitions is chosen so it can run fast + // if any potential problem is detected in MIME parser this can be changed to payload.length to run all possible + // combinations this will take at least 4 hours to run as it tests all possible combinations + size_t maxNumberOfPartitions = 3; + + for (size_t numberOfPartitions = 1; numberOfPartitions <= maxNumberOfPartitions; + numberOfPartitions += numberOfPartitionsIncrement) { + std::vector partitions(numberOfPartitions); + generateCombinationsAndRunTest(partitions, 0, payload.size(), payload, "WWWoooAAA"); + } +} + +/* + * Test: Sends the data in groups of fixed size (from 1 to the size of the payload) + * example string: abcdef, group size = 3 + * [abc],[def] + * Expected Result: Pass + */ +TEST_F(MIMEParserTest, test_fixedSizeGroups) { + std::string parts = + "--whoLetTheDogsOut\r\ncharset: UTF-8\r\nContent-Type: " + "application/json\r\n\r\n{json-content1}\r\n--whoLetTheDogsOut\r\ncharset: UTF-8\r\nContent-Type: " + "application/json\r\n\r\n{json-content2}\r\n--whoLetTheDogsOut--\r\n"; + + const std::vector testCaseExpectedData = {"{json-content1}", "{json-content2}"}; + + for (size_t groupSize = 1; groupSize <= parts.size(); groupSize++) { + const std::string boundaryHeader{BOUNDARY_HEADER_PREFIX + "whoLetTheDogsOut"}; + auto sink = std::make_shared(); + HTTP2MimeResponseDecoder decoder{sink}; + HTTP2ReceiveDataStatus status{HTTP2ReceiveDataStatus::SUCCESS}; + bool resp = decoder.onReceiveHeaderLine(boundaryHeader); + ASSERT_TRUE(resp); + decoder.onReceiveResponseCode(HTTPResponseCode::SUCCESS_OK); + + std::vector groups; + int nGroups = (parts.size() + groupSize - 1) / groupSize; + for (int i = 0; i < nGroups; i++) { + groups.push_back(""); + } + + int currentGroup = 0; + for (size_t i = 0; i < parts.size(); ++i) { + if (i != 0 && i % groupSize == 0) { + currentGroup++; + } + groups[currentGroup] = groups[currentGroup] + parts[i]; + } + + // send the data part by part + for (auto part : groups) { + status = decoder.onReceiveData(part.c_str(), part.size()); + ASSERT_EQ(status, HTTP2ReceiveDataStatus::SUCCESS); + } + ASSERT_EQ(sink->m_data, testCaseExpectedData); + } } /* @@ -270,11 +418,12 @@ TEST_F(MIMEParserTest, test_decodingBoundariesSendBoundaryWithoutCRLF) { {"\r\ncontent-type: multipart/related\r\n\r\n", 1}, {"Part2\r\n", 1}, {"--wooohooo--\r\n", 1}}; + const std::vector headers = {BOUNDARY_HEADER_PREFIX + boundary}; const std::vector> testCaseExpectedHeaders = { {{"content-type", "multipart/related"}}}; const std::vector testCaseExpectedData = {"Part1"}; runDecodingBoundariesTest( - parts, testCaseExpectedHeaders, testCaseExpectedData, boundary, HTTP2ReceiveDataStatus::ABORT); + parts, testCaseExpectedHeaders, testCaseExpectedData, headers, HTTP2ReceiveDataStatus::ABORT); } /* @@ -296,7 +445,8 @@ TEST_F(MIMEParserTest, test_decodingBoundariesSendBoundaryWithTerminatorShouldIg const std::vector> testCaseExpectedHeaders = { {{"content-type", "multipart/related"}}}; const std::vector testCaseExpectedData = {"Part1"}; - runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, boundary); + const std::vector headers = {BOUNDARY_HEADER_PREFIX + boundary}; + runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, headers); } /* @@ -318,7 +468,115 @@ TEST_F(MIMEParserTest, test_decodingBoundariesSendDuplicatedBoundaryAsHeader) { const std::vector> testCaseExpectedHeaders = { {{"content-type", "multipart/related"}}, {{"content-type", "multipart2/related"}}}; const std::vector testCaseExpectedData = {"Part1", "Part2"}; - runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, boundary); + const std::vector headers = {BOUNDARY_HEADER_PREFIX + boundary}; + runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, headers); +} + +/* + * Test: sends boundaries using the allowed chars + * Expected Result: all cases pass + */ +TEST_F(MIMEParserTest, test_decodingRandomBoundaries) { + const std::string CHARACTERS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'()+_,-.:=?"; + std::random_device random_device; + std::mt19937 generator(random_device()); + std::uniform_int_distribution<> distribution(0, CHARACTERS.size() - 1); + + for (size_t length = 5; length < 50; length++) { + std::string quotedRandomBoundary = QUOTE_CHAR; + std::string randomBoundary = ""; + for (std::size_t i = 0; i < length; ++i) { + char newChar = CHARACTERS[distribution(generator)]; + quotedRandomBoundary += newChar; + randomBoundary += newChar; + } + quotedRandomBoundary += QUOTE_CHAR; + generatePayloadAndTest(quotedRandomBoundary, randomBoundary); + generatePayloadAndTest(randomBoundary, randomBoundary); + } +} + +/* + * The RFC is not extra clear on the quoted boundary cases, so we are being lenient + * when there's a quote inside the boundary we ignore what's after the second quote and process the rest + * using the same rule as the unquoted case. + */ +TEST_F(MIMEParserTest, test_decodingBoundariesWithQuotesAndMoreHeaders) { + std::string payloadBoundary = "mybou"; + std::string unquotedBoundary = payloadBoundary + QUOTE_CHAR + "ndary"; + std::string quotedBoundary = QUOTE_CHAR + unquotedBoundary + QUOTE_CHAR; + std::vector headerAfterBoundary = {"", ";otherprop=yes", " somethingelse"}; + for (auto extraHeader : headerAfterBoundary) { + std::string boundaryHeader = quotedBoundary + extraHeader; + generatePayloadAndTest(boundaryHeader, payloadBoundary); + } +} + +/* + * Test: sends invalid boundaries + * Expected Result: all cases should fail + */ +TEST_F(MIMEParserTest, test_decodingInvalidBoundaries) { + std::vector headerAfterBoundary = {"", ";otherprop=yes", " somethingelse"}; + std::vector invalid_boundaries = { + "", + "thisstringhasmorethanseventycharacterssoitsinvalid123123123123123123123", + "^invalidchar", + "\"^invalidchar\""}; + for (auto boundary : invalid_boundaries) { + for (auto header : headerAfterBoundary) { + const std::string boundaryHeader{BOUNDARY_HEADER_PREFIX + boundary + header}; + auto sink = std::make_shared(); + HTTP2MimeResponseDecoder decoder{sink}; + ASSERT_FALSE(decoder.onReceiveHeaderLine(boundaryHeader)); + } + } +} + +/* + * Test: sends a header without boundary followed by a header with a bondary + * Expected Result: pass + */ +TEST_F(MIMEParserTest, test_decodingBoundaryAfteraNonBoundaryHeader) { + std::string boundary = "myboundary"; + const std::vector headers = {"content-type:nana;myprop:abc\r\n", BOUNDARY_HEADER_PREFIX + boundary}; + generatePayloadAndTest(headers, boundary); +} + +/* + * Test: sends a header without boundary followed by a header with a bondary + * Expected Result: pass + */ +TEST_F(MIMEParserTest, test_decodingValidBoundariesWithMoreHeaders) { + std::vector headerAfterBoundary = {"", ";otherprop=yes", " somethingelse"}; + std::string boundary = "myboundary"; + for (auto header : headerAfterBoundary) { + const std::string boundaryHeader{boundary + header}; + generatePayloadAndTest(boundaryHeader, boundary); + } +} + +/* + * Test: sends a boundary terminated by CRLF (real server case) + * Expected Result: pass + */ +TEST_F(MIMEParserTest, test_decodingBoundaryTerminatedWithCRLF) { + const std::vector> testCaseExpectedHeaders = { + {{"content-type", "application/json"}}}; + const std::vector testCaseExpectedData = {"data"}; + const std::string boundaryHeader = "content-type: multipart/related; boundary=directives\r\n"; + std::string payload = "--directives\r\ncontent-type: application/json\r\n\r\ndata"; + auto sink = std::make_shared(); + HTTP2MimeResponseDecoder decoder{sink}; + HTTP2ReceiveDataStatus status{HTTP2ReceiveDataStatus::SUCCESS}; + bool resp = decoder.onReceiveHeaderLine(boundaryHeader); + ASSERT_TRUE(resp); + decoder.onReceiveResponseCode(HTTPResponseCode::SUCCESS_OK); + // send the data part by part like the cloud does. + status = decoder.onReceiveData(payload.c_str(), payload.size()); + ASSERT_EQ(sink->m_headers, testCaseExpectedHeaders); + ASSERT_EQ(sink->m_data, testCaseExpectedData); + ASSERT_EQ(status, HTTP2ReceiveDataStatus::SUCCESS); } /* @@ -341,7 +599,8 @@ TEST_F(MIMEParserTest, test_decodingBoundariesAvs) { const std::vector> testCaseExpectedHeaders = { {{"content-type", "multipart/related"}}, {{"content-type", "multipart/related"}}}; const std::vector testCaseExpectedData = {"111122223333", "last"}; - runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, boundary); + const std::vector headers = {BOUNDARY_HEADER_PREFIX + boundary}; + runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, headers); } /* @@ -364,7 +623,8 @@ TEST_F(MIMEParserTest, test_decodingBoundariesSendFakeBoundaryAsData) { const std::vector> testCaseExpectedHeaders = { {{"content-type", "multipart/related"}}, {{"content-type", "multipart/related"}}}; const std::vector testCaseExpectedData = {"1111aa--wooohooo2222", "last"}; - runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, boundary); + const std::vector headers = {BOUNDARY_HEADER_PREFIX + boundary}; + runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, headers); } /* @@ -385,7 +645,8 @@ TEST_F(MIMEParserTest, test_decodingBoundariesSendFakeBoundaryAsOnlyData) { const std::vector> testCaseExpectedHeaders = { {{"content-type", "multipart/related"}}, {{"content-type", "multipart/related"}}}; const std::vector testCaseExpectedData = {"aa--wooohooo", "last"}; - runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, boundary); + const std::vector headers = {BOUNDARY_HEADER_PREFIX + boundary}; + runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, headers); } /* @@ -409,7 +670,8 @@ TEST_F(MIMEParserTest, test_decodingBoundariesSendBoundaryWithCRLF) { const std::vector> testCaseExpectedHeaders = { {{"content-type", "multipart/related"}}, {{"content-type", "multipart/related"}}}; const std::vector testCaseExpectedData = {"111122223333", "last"}; - runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, boundary); + const std::vector headers = {BOUNDARY_HEADER_PREFIX + boundary}; + runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, headers); } /* @@ -431,7 +693,8 @@ TEST_F(MIMEParserTest, test_decodingBoundariesSendEndBoundaryWithoutCRLF) { const std::vector> testCaseExpectedHeaders = { {{"content-type", "multipart/related"}}, {{"content-type", "multipart/related"}}}; const std::vector testCaseExpectedData = {"111122223333", "last"}; - runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, boundary); + const std::vector headers = {BOUNDARY_HEADER_PREFIX + boundary}; + runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, headers); } /* @@ -453,8 +716,9 @@ TEST_F(MIMEParserTest, test_decodingBoundariesSendBoundaryWithData) { const std::vector> testCaseExpectedHeaders = { {{"content-type", "multipart/related"}}}; const std::vector testCaseExpectedData = {"1111"}; + const std::vector headers = {BOUNDARY_HEADER_PREFIX + boundary}; runDecodingBoundariesTest( - parts, testCaseExpectedHeaders, testCaseExpectedData, boundary, HTTP2ReceiveDataStatus::ABORT); + parts, testCaseExpectedHeaders, testCaseExpectedData, headers, HTTP2ReceiveDataStatus::ABORT); } TEST_F(MIMEParserTest, test_decodingSanity) { diff --git a/AVSCommon/Utils/test/SharedDataStreamTest.cpp b/AVSCommon/Utils/test/SharedDataStreamTest.cpp index 24778342e1..791098fcff 100644 --- a/AVSCommon/Utils/test/SharedDataStreamTest.cpp +++ b/AVSCommon/Utils/test/SharedDataStreamTest.cpp @@ -1296,6 +1296,27 @@ TEST_F(SharedDataStreamTest, test_writerClosedBeforeAttachingReader) { ASSERT_EQ(error, Sds::Reader::Error::CLOSED); } +/// This tests a race condition and the first reader creation. For a new SDS with blockable writer, the first reader can +/// seek the very beginning of the stream. There was a race condition between reader and writer. Using +/// "--gtest_repeat=1000 --gtest_break_on_failure" failed this test case before the bug fix. +TEST_F(SharedDataStreamTest, test_firstReaderCanSeekSDSBeginning) { + size_t wordSize = 1; + size_t wordCount = 11000; + size_t bufferSize = Sds::calculateBufferSize(wordCount, wordSize, 3); + auto buffer = std::make_shared(bufferSize); + std::shared_ptr sds = Sds::create(buffer, wordSize, 3); + + auto writer = sds->createWriter(Sds::Writer::Policy::BLOCKING); + + Source source; + source.run(std::move(writer), 10000, 1, 0); + std::this_thread::sleep_for(std::chrono::seconds(1)); + auto reader = sds->createReader(Sds::Reader::Policy::NONBLOCKING); + // Give time to fill all buffer + std::this_thread::sleep_for(std::chrono::seconds(1)); + EXPECT_TRUE(reader->seek(0, Sds::Reader::Reference::ABSOLUTE)); +} + } // namespace test } // namespace sds } // namespace utils diff --git a/AVSGatewayManager/CMakeLists.txt b/AVSGatewayManager/CMakeLists.txt index 50a4f2c48f..f27ea5f95c 100644 --- a/AVSGatewayManager/CMakeLists.txt +++ b/AVSGatewayManager/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(AVSGatewayManager LANGUAGES CXX) -include(../build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/ApplicationUtilities/AndroidUtilities/CMakeLists.txt b/ApplicationUtilities/AndroidUtilities/CMakeLists.txt index 3f86d6f887..6c6ed73695 100644 --- a/ApplicationUtilities/AndroidUtilities/CMakeLists.txt +++ b/ApplicationUtilities/AndroidUtilities/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(AndroidUtilities LANGUAGES CXX) -include(../../build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/ApplicationUtilities/CMakeLists.txt b/ApplicationUtilities/CMakeLists.txt index 17d5fea4c0..8ada27ee38 100644 --- a/ApplicationUtilities/CMakeLists.txt +++ b/ApplicationUtilities/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1) project(ApplicationUtilities LANGUAGES CXX) -include(../build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("DefaultClient") add_subdirectory("Resources") diff --git a/ApplicationUtilities/DefaultClient/CMakeLists.txt b/ApplicationUtilities/DefaultClient/CMakeLists.txt index 9ca84e126d..d441b31aa1 100644 --- a/ApplicationUtilities/DefaultClient/CMakeLists.txt +++ b/ApplicationUtilities/DefaultClient/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(DefaultClient LANGUAGES CXX) -include(../../build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h index 7b6dde9400..7061436248 100644 --- a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h +++ b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h @@ -25,10 +25,10 @@ #include #include #include -#include -#include #include +#include #include +#include #include #include #include @@ -43,9 +43,10 @@ #include #include #include +#include #include #include -#include +#include #include #include #include @@ -68,9 +69,8 @@ #include #include #include -#include -#include -#include +#include +#include #include #include #include @@ -136,15 +136,65 @@ namespace defaultClient { */ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerInterface { public: + using DefaultClientSubsetManufactory = acsdkManufactory::Manufactory< + 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, + std::shared_ptr, + std::shared_ptr, + std::shared_ptr, + std::shared_ptr, + std::shared_ptr, + std::shared_ptr, + std::shared_ptr, + acsdkManufactory:: + Annotated, + 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, + acsdkManufactory::Annotated< + avsCommon::sdkInterfaces::endpoints::DefaultEndpointAnnotation, + avsCommon::sdkInterfaces::endpoints::EndpointBuilderInterface>, + 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>; + using DefaultClientManufactory = acsdkManufactory::Manufactory< - std::shared_ptr, + std::shared_ptr, std::shared_ptr, std::shared_ptr, + std::shared_ptr, std::shared_ptr, - std::shared_ptr, /// Applications should not use this export. + std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, + std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, @@ -164,6 +214,7 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr, std::shared_ptr, std::shared_ptr, + std::shared_ptr, std::shared_ptr, std::shared_ptr, acsdkManufactory::Annotated< @@ -173,7 +224,6 @@ 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, @@ -186,13 +236,9 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI * 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 bluetoothMediaPlayer The media player to play bluetooth content. + * @param manufactory @c Manufactory for creating various instances used by DefaultClient. * @param ringtoneMediaPlayer The media player to play Comms ringtones. - * @param systemSoundMediaPlayer The media player to play system sounds. - * @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. * @param additionalSpeakers A map of additional speakers to receive volume changes. #ifdef ENABLE_PCC * @param phoneSpeaker Interface to speaker for phone calls. @@ -209,17 +255,13 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI * @param sharedDataStream The stream to use which has the audio from microphone. #endif * @param notificationsStorage The storage interface that will be used to store notification indicators. - * @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. * @param connectionObservers Observers that can be used to be notified of connection status changes. * @param isGuiSupported Whether the device supports GUI. - * @param enabledConnectionRules The set of @c BluetoothDeviceConnectionRuleInterface instances used to - * create the Bluetooth CA. * @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 firstInteractionAudioProvider Optional object used in the first interaction started from @@ -227,13 +269,9 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI * @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 bluetoothMediaPlayer, + const std::shared_ptr& manufactory, std::shared_ptr ringtoneMediaPlayer, - std::shared_ptr systemSoundMediaPlayer, - std::shared_ptr bluetoothSpeaker, std::shared_ptr ringtoneSpeaker, - std::shared_ptr systemSoundSpeaker, const std::multimap< avsCommon::sdkInterfaces::ChannelVolumeInterface::Type, std::shared_ptr> additionalSpeakers, @@ -252,22 +290,16 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr sharedDataStream, #endif std::shared_ptr notificationsStorage, - std::shared_ptr bluetoothStorage, std::unordered_set> alexaDialogStateObservers, std::unordered_set> connectionObservers, bool isGuiSupported, - std::unordered_set> - enabledConnectionRules = std::unordered_set< - std::shared_ptr>(), avsCommon::sdkInterfaces::softwareInfo::FirmwareVersion firmwareVersion = avsCommon::sdkInterfaces::softwareInfo::INVALID_FIRMWARE_VERSION, bool sendSoftwareInfoOnConnected = false, std::shared_ptr softwareInfoSenderObserver = nullptr, - std::unique_ptr bluetoothDeviceManager = - nullptr, std::shared_ptr diagnostics = nullptr, const std::shared_ptr& externalCapabilitiesBuilder = nullptr, capabilityAgents::aip::AudioProvider firstInteractionAudioProvider = @@ -395,7 +427,7 @@ AudioInputProcessor. std::shared_ptr messageStorage, std::shared_ptr notificationsStorage, std::unique_ptr deviceSettingStorage, - std::shared_ptr bluetoothStorage, + std::shared_ptr bluetoothStorage, std::shared_ptr miscStorage, std::unordered_set> alexaDialogStateObservers, @@ -921,6 +953,16 @@ AudioInputProcessor. */ void unmuteCommsCall(); + /** + * Enable the video of local device in an active call. + */ + void enableVideo(); + + /** + * Disable the video of local device in an active call. + */ + void disableVideo(); + /* * To be called when system clock is synchronized. */ @@ -950,30 +992,40 @@ AudioInputProcessor. */ ~DefaultClient(); + /** + * Set encoding for the audio format. The new encoding will be used for future utterances. Any audio stream already + * in progress will not be affected. + * + * @param encoding The encoding format to use. + * @return @true on success, @c false on failure to set the encoding. + */ + bool setEncodingAudioFormat(avsCommon::utils::AudioFormat::Encoding encoding); + + /** + * Request for multiple audio streams with provided encodings for a single Recognize event. Calling this function + * will override any previous encoding specified by a call to @c setEncodingAudioFormat() + * @param encodings Encoding formats to use for audio streams. + * @return Result with encodings confirmed in request. + */ + capabilityAgents::aip::AudioInputProcessor::EncodingFormatResponse requestEncodingAudioFormats( + const capabilityAgents::aip::AudioInputProcessor::EncodingFormatRequest& encodings); + private: /** * Initializes the SDK and "glues" all the components together. * * @param manufactory @c Manufactory for creating various instances used by DefaultlClient. - * @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 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 notificationsStorage The storage interface that will be used to store notification indicators. - * @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. * @param connectionObservers Observers that can be used to be notified of connection status changes. * @param isGuiSupported Whether the device supports GUI. - * @param enabledConnectionRules The set of @c BluetoothDeviceConnectionRuleInterface instances used to - * create the Bluetooth CA. * @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 firstInteractionAudioProvider Optional object used in the first interaction started from @@ -982,12 +1034,8 @@ AudioInputProcessor. */ bool initialize( const std::shared_ptr& manufactory, - std::shared_ptr bluetoothMediaPlayer, std::shared_ptr ringtoneMediaPlayer, - std::shared_ptr systemSoundMediaPlayer, - std::shared_ptr bluetoothSpeaker, std::shared_ptr ringtoneSpeaker, - std::shared_ptr systemSoundSpeaker, const std::multimap< avsCommon::sdkInterfaces::ChannelVolumeInterface::Type, std::shared_ptr> additionalSpeakers, @@ -1006,18 +1054,14 @@ AudioInputProcessor. std::shared_ptr sharedDataStream, #endif std::shared_ptr notificationsStorage, - std::shared_ptr bluetoothStorage, std::unordered_set> alexaDialogStateObservers, std::unordered_set> connectionObservers, bool isGuiSupported, - std::unordered_set> - enabledConnectionRules, avsCommon::sdkInterfaces::softwareInfo::FirmwareVersion firmwareVersion, bool sendSoftwareInfoOnConnected, std::shared_ptr softwareInfoSenderObserver, - std::unique_ptr bluetoothDeviceManager, std::shared_ptr diagnostics, const std::shared_ptr& externalCapabilitiesBuilder, capabilityAgents::aip::AudioProvider firstInteractionAudioProvider); @@ -1070,10 +1114,10 @@ AudioInputProcessor. std::shared_ptr m_apiGatewayCapabilityAgent; /// The alerts capability agent. - std::shared_ptr m_alertsCapabilityAgent; + std::shared_ptr m_alertsCapabilityAgent; - /// The bluetooth capability agent. - std::shared_ptr m_bluetooth; + /// The bluetooth notifier. + std::shared_ptr m_bluetoothNotifier; /// The interaction model capability agent. std::shared_ptr m_interactionCapabilityAgent; @@ -1139,9 +1183,6 @@ AudioInputProcessor. /// The RegistrationManager used to control customer registration. std::shared_ptr m_registrationManager; - /// An instance of the system sounds player. - std::shared_ptr m_systemSoundPlayer; - /// Module responsible for managing device settings. std::shared_ptr m_deviceSettingsManager; @@ -1166,8 +1207,8 @@ AudioInputProcessor. /// Diagnostic interface. std::shared_ptr m_diagnostics; - /// The system clock monitor. - std::shared_ptr m_systemClockMonitor; + /// The system clock monitor manager. + std::shared_ptr m_systemClockMonitor; /// The list of objects to be shutdown. Shutdown will occur in the reverse order of occurrence. std::list> m_shutdownObjects; diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h index d450b35fc6..8ca46b839c 100644 --- a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h +++ b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h @@ -20,11 +20,16 @@ #include #include +#include #include +#include +#include +#include #include #include #include #include +#include #include #include #include @@ -42,6 +47,8 @@ #include #include #include +#include +#include #include #include #include @@ -51,7 +58,6 @@ #include #include #include -#include #include "DefaultClient/EqualizerRuntimeSetup.h" #include "DefaultClient/StubApplicationAudioPipelineFactory.h" @@ -66,11 +72,12 @@ namespace defaultClient { * non-manufactory method of initialization. */ 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, /// Applications should not use this export. + std::shared_ptr, std::shared_ptr, std::shared_ptr, std::shared_ptr, @@ -93,6 +100,7 @@ using DefaultClientComponent = acsdkManufactory::Component< std::shared_ptr, std::shared_ptr, std::shared_ptr, + std::shared_ptr, std::shared_ptr, std::shared_ptr, acsdkManufactory::Annotated< @@ -102,7 +110,7 @@ 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, @@ -143,7 +151,11 @@ DefaultClientComponent getComponent( const std::shared_ptr& deviceSettingStorage, bool startAlertSchedulingOnInitialization, const std::shared_ptr& audioFactory, - const std::shared_ptr& alertStorage); + const std::shared_ptr& alertStorage, + const std::shared_ptr& bluetoothDeviceManager, + const std::shared_ptr& bluetoothStorage, + const std::shared_ptr& + bluetoothConnectionRulesProvider); } // namespace defaultClient } // namespace alexaClientSDK diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/ExternalCapabilitiesBuilderInterface.h b/ApplicationUtilities/DefaultClient/include/DefaultClient/ExternalCapabilitiesBuilderInterface.h index a3a8a9875b..079259a83b 100644 --- a/ApplicationUtilities/DefaultClient/include/DefaultClient/ExternalCapabilitiesBuilderInterface.h +++ b/ApplicationUtilities/DefaultClient/include/DefaultClient/ExternalCapabilitiesBuilderInterface.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -157,6 +158,7 @@ class ExternalCapabilitiesBuilderInterface { #endif * @param powerResourceManager Object to manage power resource. * @param softwareComponentReporter Object to report adapters' versions. + * @param playbackRouter Object to route local playback control command. * @return A list with all capabilities as well as objects that require explicit shutdown. Shutdown will be * performed in the reverse order of occurrence. */ @@ -185,7 +187,8 @@ class ExternalCapabilitiesBuilderInterface { std::shared_ptr sharedDataStream, #endif std::shared_ptr powerResourceManager, - std::shared_ptr softwareComponentReporter) = 0; + std::shared_ptr softwareComponentReporter, + std::shared_ptr playbackRouter) = 0; }; } // namespace defaultClient diff --git a/ApplicationUtilities/DefaultClient/src/CMakeLists.txt b/ApplicationUtilities/DefaultClient/src/CMakeLists.txt index e31b7880a8..b3980b20d1 100644 --- a/ApplicationUtilities/DefaultClient/src/CMakeLists.txt +++ b/ApplicationUtilities/DefaultClient/src/CMakeLists.txt @@ -55,9 +55,11 @@ target_link_libraries(DefaultClient acsdkAlertsInterfaces acsdkAudioPlayer acsdkBluetooth + acsdkBluetoothInterfaces acsdkEqualizer acsdkExternalMediaPlayer acsdkStartupManagerInterfaces + acsdkSystemClockMonitorInterfaces ) if (CAPTIONS) diff --git a/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp b/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp index e0e5fc2ce8..f528403e2e 100644 --- a/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp +++ b/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp @@ -14,12 +14,13 @@ */ #include +#include #include #include +#include #include #include #include -#include #include #include #include @@ -33,6 +34,8 @@ #include #include #include +#include +#include #ifdef ENABLE_OPUS #include @@ -49,11 +52,6 @@ #include #endif -#ifdef BLUETOOTH_BLUEZ -#include -#include -#endif - #include "DefaultClient/DefaultClient.h" #include "DefaultClient/DefaultClientComponent.h" #include "DefaultClient/StubApplicationAudioPipelineFactory.h" @@ -85,13 +83,9 @@ static const std::string VISUAL_CHANNEL_CONFIG_KEY = "visualChannels"; #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) std::unique_ptr DefaultClient::create( - const std::shared_ptr& manufactory, - std::shared_ptr bluetoothMediaPlayer, + const std::shared_ptr& manufactory, std::shared_ptr ringtoneMediaPlayer, - std::shared_ptr systemSoundMediaPlayer, - std::shared_ptr bluetoothSpeaker, std::shared_ptr ringtoneSpeaker, - std::shared_ptr systemSoundSpeaker, const std::multimap< avsCommon::sdkInterfaces::ChannelVolumeInterface::Type, std::shared_ptr> additionalSpeakers, @@ -110,30 +104,22 @@ std::unique_ptr DefaultClient::create( std::shared_ptr sharedDataStream, #endif std::shared_ptr notificationsStorage, - std::shared_ptr bluetoothStorage, std::unordered_set> alexaDialogStateObservers, std::unordered_set> connectionObservers, bool isGuiSupported, - std::unordered_set> - enabledConnectionRules, avsCommon::sdkInterfaces::softwareInfo::FirmwareVersion firmwareVersion, bool sendSoftwareInfoOnConnected, std::shared_ptr softwareInfoSenderObserver, - std::unique_ptr bluetoothDeviceManager, std::shared_ptr diagnostics, const std::shared_ptr& externalCapabilitiesBuilder, capabilityAgents::aip::AudioProvider firstInteractionAudioProvider) { std::unique_ptr defaultClient(new DefaultClient()); if (!defaultClient->initialize( manufactory, - bluetoothMediaPlayer, ringtoneMediaPlayer, - systemSoundMediaPlayer, - bluetoothSpeaker, ringtoneSpeaker, - systemSoundSpeaker, additionalSpeakers, #ifdef ENABLE_PCC phoneSpeaker, @@ -150,15 +136,12 @@ std::unique_ptr DefaultClient::create( sharedDataStream, #endif notificationsStorage, - bluetoothStorage, alexaDialogStateObservers, connectionObservers, isGuiSupported, - enabledConnectionRules, firmwareVersion, sendSoftwareInfoOnConnected, softwareInfoSenderObserver, - std::move(bluetoothDeviceManager), diagnostics, externalCapabilitiesBuilder, firstInteractionAudioProvider)) { @@ -213,7 +196,7 @@ std::unique_ptr DefaultClient::create( std::shared_ptr messageStorage, std::shared_ptr notificationsStorage, std::unique_ptr deviceSettingStorage, - std::shared_ptr bluetoothStorage, + std::shared_ptr bluetoothStorage, std::shared_ptr miscStorage, std::unordered_set> alexaDialogStateObservers, @@ -247,6 +230,9 @@ std::unique_ptr DefaultClient::create( equalizerRuntimeSetup = std::make_shared(false); } + auto bluetoothConnectionRulesProvider = + std::make_shared(enabledConnectionRules); + auto stubAudioPipelineFactory = StubApplicationAudioPipelineFactory::create(channelVolumeFactory); if (!stubAudioPipelineFactory) { ACSDK_ERROR(LX("createFailed").d("reason", "failed to create audio pipeline")); @@ -260,6 +246,13 @@ std::unique_ptr DefaultClient::create( acsdkAlerts::ALERTS_MEDIA_PLAYER_NAME, alertsMediaPlayer, alertsSpeaker); stubAudioPipelineFactory->addApplicationMediaInterfaces( capabilityAgents::speechSynthesizer::SPEAK_MEDIA_PLAYER_NAME, speakMediaPlayer, speakSpeaker); + stubAudioPipelineFactory->addApplicationMediaInterfaces( + applicationUtilities::systemSoundPlayer::SYSTEM_SOUND_MEDIA_PLAYER_NAME, + systemSoundMediaPlayer, + systemSoundSpeaker); + stubAudioPipelineFactory->addApplicationMediaInterfaces( + acsdkBluetooth::BLUETOOTH_MEDIA_PLAYER_NAME, bluetoothMediaPlayer, bluetoothSpeaker); + for (const auto& adapter : adapterCreationMap) { auto mediaPlayerIt = externalMusicProviderMediaPlayers.find(adapter.first); auto speakerIt = externalMusicProviderSpeakers.find(adapter.first); @@ -315,7 +308,10 @@ std::unique_ptr DefaultClient::create( std::move(deviceSettingStorage), startAlertSchedulingOnInitialization, audioFactory, - std::move(alertStorage)); + std::move(alertStorage), + std::move(bluetoothDeviceManager), + std::move(bluetoothStorage), + bluetoothConnectionRulesProvider); auto manufactory = DefaultClientManufactory::create(component); auto speakerManager = manufactory->get>(); @@ -336,12 +332,8 @@ std::unique_ptr DefaultClient::create( return create( std::move(manufactory), - bluetoothMediaPlayer, ringtoneMediaPlayer, - systemSoundMediaPlayer, - bluetoothSpeaker, ringtoneSpeaker, - systemSoundSpeaker, additionalSpeakers, #ifdef ENABLE_PCC phoneSpeaker, @@ -358,15 +350,12 @@ std::unique_ptr DefaultClient::create( sharedDataStream, #endif notificationsStorage, - bluetoothStorage, alexaDialogStateObservers, connectionObservers, isGuiSupported, - enabledConnectionRules, firmwareVersion, sendSoftwareInfoOnConnected, softwareInfoSenderObserver, - std::move(bluetoothDeviceManager), diagnostics, externalCapabilitiesBuilder, firstInteractionAudioProvider); @@ -374,12 +363,8 @@ std::unique_ptr DefaultClient::create( bool DefaultClient::initialize( const std::shared_ptr& manufactory, - std::shared_ptr bluetoothMediaPlayer, std::shared_ptr ringtoneMediaPlayer, - std::shared_ptr systemSoundMediaPlayer, - std::shared_ptr bluetoothSpeaker, std::shared_ptr ringtoneSpeaker, - std::shared_ptr systemSoundSpeaker, const std::multimap< avsCommon::sdkInterfaces::ChannelVolumeInterface::Type, std::shared_ptr> additionalSpeakers, @@ -398,36 +383,22 @@ bool DefaultClient::initialize( std::shared_ptr sharedDataStream, #endif std::shared_ptr notificationsStorage, - std::shared_ptr bluetoothStorage, std::unordered_set> alexaDialogStateObservers, std::unordered_set> connectionObservers, bool isGuiSupported, - std::unordered_set> - enabledConnectionRules, avsCommon::sdkInterfaces::softwareInfo::FirmwareVersion firmwareVersion, bool sendSoftwareInfoOnConnected, std::shared_ptr softwareInfoSenderObserver, - std::unique_ptr bluetoothDeviceManager, std::shared_ptr diagnostics, const std::shared_ptr& externalCapabilitiesBuilder, capabilityAgents::aip::AudioProvider firstInteractionAudioProvider) { - if (!bluetoothMediaPlayer) { - ACSDK_ERROR(LX("initializeFailed").d("reason", "nullBluetoothMediaPlayer")); - return false; - } - if (!ringtoneMediaPlayer) { ACSDK_ERROR(LX("initializeFailed").d("reason", "nullRingtoneMediaPlayer")); return false; } - if (!systemSoundMediaPlayer) { - ACSDK_ERROR(LX("initializeFailed").d("reason", "nullSystemSoundMediaPlayer")); - return false; - } - // Initialize various locals from manufactory. auto metricRecorder = manufactory->get>(); if (!metricRecorder) { @@ -603,6 +574,7 @@ bool DefaultClient::initialize( m_shutdownManager = manufactory->get>(); if (!m_shutdownManager) { ACSDK_ERROR(LX("initializeFailed").m("Failed to get ShutdownManager!")); + return false; } m_certifiedSender = manufactory->get>(); @@ -624,18 +596,30 @@ bool DefaultClient::initialize( return false; } - m_systemClockMonitor = manufactory->get>(); + m_systemClockMonitor = + manufactory->get>(); if (!m_systemClockMonitor) { - ACSDK_ERROR(LX("initializeFailed").d("reason", "nullSystemClockMonitor")); + ACSDK_ERROR(LX("initializeFailed").d("reason", "nullSystemClockMonitorManager")); return false; } - m_alertsCapabilityAgent = manufactory->get>(); + m_alertsCapabilityAgent = + manufactory->get>(); if (!m_alertsCapabilityAgent) { ACSDK_ERROR(LX("initializeFailed").d("reason", "unableToCreateAlertsCapabilityAgent")); return false; } + m_bluetoothNotifier = manufactory->get>(); + if (!m_bluetoothNotifier) { +#ifdef BLUETOOTH_ENABLED + ACSDK_ERROR(LX("initializeFailed").d("reason", "unableToCreateBluetoothNotifier")); + return false; +#else + ACSDK_DEBUG5(LX("nullBluetoothNotifier").m("Bluetooth disabled")); +#endif + } + m_dialogUXStateAggregator = std::make_shared(metricRecorder); m_softwareReporterCapabilityAgent = @@ -654,14 +638,10 @@ bool DefaultClient::initialize( m_connectionManager->addConnectionStatusObserver(observer); } - m_connectionManager->addMessageObserver(m_dialogUXStateAggregator); - for (auto observer : alexaDialogStateObservers) { m_dialogUXStateAggregator->addObserver(observer); } - m_connectionManager->addMessageObserver(m_dialogUXStateAggregator); - /* * Creating the Directive Sequencer - This is the component that deals with * the sequencing and ordering of @@ -704,6 +684,7 @@ bool DefaultClient::initialize( ACSDK_ERROR(LX("initializeFailed").d("reason", "endpointRegistrationManagerCreateFailed")); return false; } + localeAssetsManager->setEndpointRegistrationManager(m_endpointRegistrationManager); /* * Creating the User Inactivity Monitor - This component is responsibly for @@ -717,14 +698,13 @@ bool DefaultClient::initialize( return false; } - m_systemSoundPlayer = applicationUtilities::systemSoundPlayer::SystemSoundPlayer::create( - systemSoundMediaPlayer, audioFactory->systemSounds()); - auto wakeWordConfirmationSetting = m_deviceSettingsManager->getSetting(); auto speechConfirmationSetting = m_deviceSettingsManager->getSetting(); auto wakeWordsSetting = m_deviceSettingsManager->getSetting(); + auto capabilityChangeNotifier = std::make_shared(); + capabilityChangeNotifier->addObserver(localeAssetsManager); /* * Creating the Audio Input Processor - This component is the Capability Agent @@ -739,10 +719,11 @@ bool DefaultClient::initialize( m_dialogUXStateAggregator, m_exceptionSender, m_userInactivityMonitor, - m_systemSoundPlayer, + manufactory->get>(), localeAssetsManager, wakeWordConfirmationSetting, speechConfirmationSetting, + capabilityChangeNotifier, wakeWordsSetting, std::make_shared(std::make_shared()), firstInteractionAudioProvider, @@ -758,10 +739,11 @@ bool DefaultClient::initialize( m_dialogUXStateAggregator, m_exceptionSender, m_userInactivityMonitor, - m_systemSoundPlayer, + manufactory->get>(), localeAssetsManager, wakeWordConfirmationSetting, speechConfirmationSetting, + capabilityChangeNotifier, wakeWordsSetting, nullptr, firstInteractionAudioProvider, @@ -809,7 +791,7 @@ bool DefaultClient::initialize( m_speechSynthesizer->addObserver(m_dialogUXStateAggregator); // create @c SpeakerInterfaces for each @c Type - std::vector> allAvsSpeakers{systemSoundSpeaker}; + std::vector> allAvsSpeakers{}; // parse additional Speakers into the right speaker list. for (const auto& it : additionalSpeakers) { if (ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME == it.first) { @@ -838,11 +820,6 @@ bool DefaultClient::initialize( allAvsChannelVolumeInterfaces.push_back(channelVolumeFactory->createChannelVolumeInterface(it)); } - // create @c ChannelVolumeInterface for bluetoothSpeaker (later used by Bluetooth CapabilityAgent) - std::shared_ptr bluetoothChannelVolumeInterface = - channelVolumeFactory->createChannelVolumeInterface(bluetoothSpeaker); - allAvsChannelVolumeInterfaces.push_back(bluetoothChannelVolumeInterface); - // create @c ChannelVolumeInterface for ringtoneSpeaker std::shared_ptr ringtoneChannelVolumeInterface = channelVolumeFactory->createChannelVolumeInterface(ringtoneSpeaker); @@ -1075,7 +1052,8 @@ bool DefaultClient::initialize( * publishing information about the System * capability agent. */ - auto systemCapabilityProvider = capabilityAgents::system::SystemCapabilityProvider::create(localeAssetsManager); + auto systemCapabilityProvider = + capabilityAgents::system::SystemCapabilityProvider::create(localeAssetsManager, capabilityChangeNotifier); if (!systemCapabilityProvider) { ACSDK_ERROR(LX("initializeFailed").d("reason", "unableToCreateSystemCapabilityProvider")); return false; @@ -1095,39 +1073,6 @@ bool DefaultClient::initialize( } #endif - if (bluetoothDeviceManager) { - ACSDK_DEBUG5(LX(__func__).m("Creating Bluetooth CA")); - - // Create a temporary pointer to the eventBus inside of - // bluetoothDeviceManager so that - // the unique ptr for bluetoothDeviceManager can be moved. - auto eventBus = bluetoothDeviceManager->getEventBus(); - - auto bluetoothMediaInputTransformer = - acsdkBluetooth::BluetoothMediaInputTransformer::create(eventBus, m_playbackRouter); - - /* - * Creating the Bluetooth Capability Agent - This component is responsible - * for handling directives from AVS - * regarding bluetooth functionality. - */ - m_bluetooth = acsdkBluetooth::Bluetooth::create( - m_contextManager, - m_audioFocusManager, - m_connectionManager, - m_exceptionSender, - std::move(bluetoothStorage), - std::move(bluetoothDeviceManager), - std::move(eventBus), - bluetoothMediaPlayer, - customerDataManager, - enabledConnectionRules, - bluetoothChannelVolumeInterface, - bluetoothMediaInputTransformer); - } else { - ACSDK_DEBUG5(LX("bluetoothCapabilityAgentDisabled").d("reason", "nullBluetoothDeviceManager")); - } - m_apiGatewayCapabilityAgent = capabilityAgents::apiGateway::ApiGatewayCapabilityAgent::create(m_avsGatewayManager, m_exceptionSender); if (!m_apiGatewayCapabilityAgent) { @@ -1197,10 +1142,6 @@ bool DefaultClient::initialize( m_defaultEndpointBuilder->withCapability(m_notificationsCapabilityAgent, m_notificationsCapabilityAgent); m_defaultEndpointBuilder->withCapability(m_interactionCapabilityAgent, m_interactionCapabilityAgent); - if (m_bluetooth) { - m_defaultEndpointBuilder->withCapability(m_bluetooth, m_bluetooth); - } - if (m_equalizerCapabilityAgent) { m_defaultEndpointBuilder->withCapability(m_equalizerCapabilityAgent, m_equalizerCapabilityAgent); } @@ -1252,7 +1193,8 @@ bool DefaultClient::initialize( sharedDataStream, #endif powerResourceManager, - m_softwareReporterCapabilityAgent); + m_softwareReporterCapabilityAgent, + m_playbackRouter); for (auto& capability : externalCapabilities.first) { if (capability.configuration.hasValue()) { m_defaultEndpointBuilder->withCapability(capability.configuration.value(), capability.directiveHandler); @@ -1466,19 +1408,18 @@ void DefaultClient::setCaptionMediaPlayers( void DefaultClient::addBluetoothDeviceObserver( std::shared_ptr observer) { - if (!m_bluetooth) { - ACSDK_DEBUG5(LX(__func__).m("bluetooth is disabled, not adding observer")); + if (!m_bluetoothNotifier) { + ACSDK_DEBUG5(LX(__func__).m("Bluetooth disabled")); return; } - m_bluetooth->addObserver(observer); + m_bluetoothNotifier->addObserver(observer); } void DefaultClient::removeBluetoothDeviceObserver( std::shared_ptr observer) { - if (!m_bluetooth) { - return; + if (m_bluetoothNotifier) { + m_bluetoothNotifier->removeObserver(observer); } - m_bluetooth->removeObserver(observer); } #ifdef ENABLE_REVOKE_AUTH @@ -1762,8 +1703,20 @@ void DefaultClient::unmuteCommsCall() { } } +void DefaultClient::enableVideo() { + if (m_callManager) { + m_callManager->enableVideo(); + } +} + +void DefaultClient::disableVideo() { + if (m_callManager) { + m_callManager->disableVideo(); + } +} + void DefaultClient::onSystemClockSynchronized() { - m_systemClockMonitor->notifySystemClockSynchronized(); + m_systemClockMonitor->onSystemClockSynchronized(); } void DefaultClient::registerExternalMediaPlayerAdapterHandler( @@ -1827,10 +1780,6 @@ DefaultClient::~DefaultClient() { ACSDK_DEBUG5(LX("NotificationsRendererShutdown.")); m_notificationsRenderer->shutdown(); } - if (m_bluetooth) { - ACSDK_DEBUG5(LX("BluetoothShutdown.")); - m_bluetooth->shutdown(); - } if (m_userInactivityMonitor) { ACSDK_DEBUG5(LX("UserInactivityMonitorShutdown.")); @@ -1888,5 +1837,13 @@ DefaultClient::~DefaultClient() { } } +bool DefaultClient::setEncodingAudioFormat(AudioFormat::Encoding encoding) { + return m_audioInputProcessor->setEncodingAudioFormat(encoding); +} + +capabilityAgents::aip::AudioInputProcessor::EncodingFormatResponse DefaultClient::requestEncodingAudioFormats( + const capabilityAgents::aip::AudioInputProcessor::EncodingFormatRequest& encodings) { + return m_audioInputProcessor->requestEncodingAudioFormats(encodings); +} } // namespace defaultClient } // namespace alexaClientSDK diff --git a/ApplicationUtilities/DefaultClient/src/DefaultClientComponent.cpp b/ApplicationUtilities/DefaultClient/src/DefaultClientComponent.cpp index 7731d8f4d3..15ae75ceae 100644 --- a/ApplicationUtilities/DefaultClient/src/DefaultClientComponent.cpp +++ b/ApplicationUtilities/DefaultClient/src/DefaultClientComponent.cpp @@ -16,13 +16,15 @@ #include #include #include +#include #include #include #include #include -#include #include #include +#include +#include #include #include #include @@ -40,8 +42,10 @@ #include #include #include +#include #include #include +#include #include #include "DefaultClient/DefaultClientComponent.h" @@ -57,6 +61,7 @@ using namespace acsdkAlexaEventProcessedNotifierInterfaces; using namespace acsdkApplicationAudioPipelineFactoryInterfaces; using namespace acsdkManufactory; using namespace acsdkShutdownManagerInterfaces; +using namespace applicationUtilities; using namespace avsCommon::avs::attachment; using namespace avsCommon::utils; using namespace avsCommon::utils::libcurlUtils; @@ -198,7 +203,18 @@ DefaultClientComponent getComponent( const std::shared_ptr& deviceSettingStorage, bool startAlertSchedulingOnInitialization, const std::shared_ptr& audioFactory, - const std::shared_ptr& alertStorage) { + const std::shared_ptr& alertStorage, + const std::shared_ptr& bluetoothDeviceManager, + const std::shared_ptr& bluetoothStorage, + const std::shared_ptr& + bluetoothConnectionRulesProvider) { + std::shared_ptr bluetoothEventBus; + if (bluetoothDeviceManager) { + bluetoothEventBus = bluetoothDeviceManager->getEventBus(); + } else { + bluetoothEventBus = nullptr; + } + return ComponentAccumulator<>() /// Instead of using factory methods to instantiate these interfaces, DefaultClientComponent accepts pre-made /// implementations and adds them to the manufactory. @@ -223,12 +239,18 @@ DefaultClientComponent getComponent( .addInstance(systemTimeZone) .addInstance(audioFactory) .addInstance(alertStorage) + .addInstance(bluetoothConnectionRulesProvider) + .addInstance(bluetoothDeviceManager) + .addInstance(bluetoothEventBus) + .addInstance(bluetoothStorage) .addRetainedFactory(getCreateApplicationAudioPipelineFactory(stubAudioPipelineFactory)) .addRetainedFactory(getCreateDeviceSettingStorageInterface(deviceSettingStorage)) /// Baseline SDK components. Applications are not expected to modify these. .addComponent(acsdkDeviceSettingsManager::getComponent()) .addComponent(acsdkShared::getComponent()) + .addRetainedFactory(acsdkSystemClockMonitor::SystemClockMonitor::createSystemClockMonitorInterface) + .addRetainedFactory(acsdkSystemClockMonitor::SystemClockNotifier::createSystemClockNotifierInterface) .addRetainedFactory(afml::interruptModel::InterruptModel::createInterruptModel) .addComponent(afml::getComponent()) .addRetainedFactory(AlexaInterfaceMessageSender::createAlexaInterfaceMessageSender) @@ -246,6 +268,7 @@ DefaultClientComponent getComponent( .addRetainedFactory(DefaultSetCurlOptionsCallbackFactory::createSetCurlOptionsCallbackFactoryInterface) .addRetainedFactory(HTTPContentFetcherFactory::createHTTPContentFetcherInterfaceFactoryInterface) .addRetainedFactory(getCreateMessageRouter(messageRouterFactory)) + .addRetainedFactory(systemSoundPlayer::SystemSoundPlayer::createSystemSoundPlayerInterface) .addUniqueFactory(capabilitiesDelegate::storage::SQLiteCapabilitiesDelegateStorage:: createCapabilitiesDelegateStorageInterface) @@ -255,6 +278,7 @@ DefaultClientComponent getComponent( /// Capability Agents. Most CAs are still instantiated in DefaultClient.cpp. .addComponent(acsdkAlerts::getComponent(startAlertSchedulingOnInitialization)) .addComponent(acsdkAudioPlayer::getBackwardsCompatibleComponent()) + .addComponent(acsdkBluetooth::getComponent()) .addComponent(acsdkDoNotDisturb::getComponent()) .addComponent(acsdkExternalMediaPlayer::getBackwardsCompatibleComponent(adapterCreationMap)) .addComponent(capabilityAgents::playbackController::getComponent()) diff --git a/ApplicationUtilities/Resources/Audio/src/AlertsAudioFactory.cpp b/ApplicationUtilities/Resources/Audio/src/AlertsAudioFactory.cpp index 7baf8ff78b..5534eef77b 100644 --- a/ApplicationUtilities/Resources/Audio/src/AlertsAudioFactory.cpp +++ b/ApplicationUtilities/Resources/Audio/src/AlertsAudioFactory.cpp @@ -17,6 +17,7 @@ #include +#include "Audio/Data/med_alerts_notification_03.mp3.h" #include "Audio/Data/med_system_alerts_melodic_01.mp3.h" #include "Audio/Data/med_system_alerts_melodic_01_short.wav.h" #include "Audio/Data/med_system_alerts_melodic_02.mp3.h" @@ -56,14 +57,14 @@ static std::pair, const avsCommon::utils::MediaTyp static std::pair, const avsCommon::utils::MediaType> reminderDefaultFactory() { return std::make_pair( avsCommon::utils::stream::streamFromData( - data::med_system_alerts_melodic_01_mp3, sizeof(data::med_system_alerts_melodic_01_mp3)), - avsCommon::utils::MimeTypeToMediaType(data::med_system_alerts_melodic_01_mp3_mimetype)); + data::med_alerts_notification_03_mp3, sizeof(data::med_alerts_notification_03_mp3)), + avsCommon::utils::MimeTypeToMediaType(data::med_alerts_notification_03_mp3_mimetype)); } static std::pair, const avsCommon::utils::MediaType> reminderShortFactory() { return std::make_pair( avsCommon::utils::stream::streamFromData( - data::med_system_alerts_melodic_01_short_wav, sizeof(data::med_system_alerts_melodic_01_short_wav)), - avsCommon::utils::MimeTypeToMediaType(data::med_system_alerts_melodic_01_short_wav_mimetype)); + data::med_alerts_notification_03_mp3, sizeof(data::med_alerts_notification_03_mp3)), + avsCommon::utils::MimeTypeToMediaType(data::med_alerts_notification_03_mp3_mimetype)); } std::function, const avsCommon::utils::MediaType>()> AlertsAudioFactory:: diff --git a/ApplicationUtilities/SystemSoundPlayer/include/SystemSoundPlayer/SystemSoundPlayer.h b/ApplicationUtilities/SystemSoundPlayer/include/SystemSoundPlayer/SystemSoundPlayer.h index 2de4d2a53c..0c21cec426 100644 --- a/ApplicationUtilities/SystemSoundPlayer/include/SystemSoundPlayer/SystemSoundPlayer.h +++ b/ApplicationUtilities/SystemSoundPlayer/include/SystemSoundPlayer/SystemSoundPlayer.h @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include #include @@ -29,6 +31,9 @@ namespace alexaClientSDK { namespace applicationUtilities { namespace systemSoundPlayer { +/// String to identify the system sound media player to render system sounds. +static const constexpr char* SYSTEM_SOUND_MEDIA_PLAYER_NAME = "SystemSoundMediaPlayer"; + /** * This class implements the @c SystemSoundPlayerInterface. This class is responsible for playing the system sounds that * Alexa devices make. @@ -37,9 +42,23 @@ class SystemSoundPlayer : public avsCommon::sdkInterfaces::SystemSoundPlayerInterface , public avsCommon::utils::mediaPlayer::MediaPlayerObserverInterface { public: + /** + * Creates a new @c SystemSoundPlayerInterface instance. + * + * @param audioPipelineFactory The audio pipeline factory to create the media player and related interfaces. + * @param audioFactory The audio factory that produces the system sound streams. + * + * @return A @c std::shared_ptr to the new @c SystemSoundPlayer instance or nullptr if invalid arguments. + */ + static std::shared_ptr createSystemSoundPlayerInterface( + std::shared_ptr + audioPipelineFactory, + std::shared_ptr audioFactory); + /** * Creates a new @c SystemSoundPlayer instance. * + * @deprecated Use createSystemSoundPlayerInterface. * @param mediaPlayer The mediaPlayer that will play the system sound audio streams. * @param soundPlayerAudioFactory The audio factory that produces the system sound streams * diff --git a/ApplicationUtilities/SystemSoundPlayer/src/SystemSoundPlayer.cpp b/ApplicationUtilities/SystemSoundPlayer/src/SystemSoundPlayer.cpp index d3dc701763..21478a5b0e 100644 --- a/ApplicationUtilities/SystemSoundPlayer/src/SystemSoundPlayer.cpp +++ b/ApplicationUtilities/SystemSoundPlayer/src/SystemSoundPlayer.cpp @@ -40,6 +40,46 @@ static std::shared_future getFalseFuture() { return errPromise.get_future(); } +std::shared_ptr SystemSoundPlayer:: + createSystemSoundPlayerInterface( + std::shared_ptr + audioPipelineFactory, + std::shared_ptr audioFactory) { + if (!audioPipelineFactory) { + ACSDK_ERROR(LX("createSystemSoundPlayerInterfaceFailed").d("reason", "nullMediaPlayer")); + return nullptr; + } + if (!audioFactory) { + ACSDK_ERROR(LX("createSystemSoundPlayerInterfaceFailed").d("reason", "nullAudioFactory")); + return nullptr; + } + + auto applicationMediaInterfaces = + audioPipelineFactory->createApplicationMediaInterfaces(SYSTEM_SOUND_MEDIA_PLAYER_NAME); + if (!applicationMediaInterfaces) { + ACSDK_ERROR(LX("createSystemSoundPlayerInterfaceFailed").d("reason", "nullApplicationMediaInterfaces")); + return nullptr; + } + + auto mediaPlayer = applicationMediaInterfaces->mediaPlayer; + if (!mediaPlayer) { + ACSDK_ERROR(LX("createSystemSoundPlayerInterfaceFailed").d("reason", "nullMediaPlayer")); + return nullptr; + } + + auto systemSoundsAudioFactory = audioFactory->systemSounds(); + if (!systemSoundsAudioFactory) { + ACSDK_ERROR(LX("createSystemSoundPlayerInterfaceFailed").d("reason", "nullSystemSoundsAudioFactory")); + return nullptr; + } + + auto systemSoundPlayer = + std::shared_ptr(new SystemSoundPlayer(mediaPlayer, systemSoundsAudioFactory)); + mediaPlayer->addObserver(systemSoundPlayer); + + return systemSoundPlayer; +} + std::shared_ptr SystemSoundPlayer::create( std::shared_ptr mediaPlayer, std::shared_ptr soundPlayerAudioFactory) { diff --git a/ApplicationUtilities/SystemSoundPlayer/test/CMakeLists.txt b/ApplicationUtilities/SystemSoundPlayer/test/CMakeLists.txt index 526a05d8ef..28660e8b41 100644 --- a/ApplicationUtilities/SystemSoundPlayer/test/CMakeLists.txt +++ b/ApplicationUtilities/SystemSoundPlayer/test/CMakeLists.txt @@ -4,4 +4,4 @@ set(INCLUDE_PATH "${SystemSoundPlayer_INCLUDE_DIRS}" "${AVSCommon_SOURCE_DIR}/Utils/test") -discover_unit_tests("${INCLUDE_PATH}" "SystemSoundPlayer;UtilsCommonTestLib;SDKInterfacesTests") +discover_unit_tests("${INCLUDE_PATH}" "SystemSoundPlayer;UtilsCommonTestLib;SDKInterfacesTests;ApplicationAudioPipelineFactoryTestLib") diff --git a/ApplicationUtilities/SystemSoundPlayer/test/SystemSoundPlayerTest.cpp b/ApplicationUtilities/SystemSoundPlayer/test/SystemSoundPlayerTest.cpp index fcd944cbf4..62f4b503c8 100644 --- a/ApplicationUtilities/SystemSoundPlayer/test/SystemSoundPlayerTest.cpp +++ b/ApplicationUtilities/SystemSoundPlayer/test/SystemSoundPlayerTest.cpp @@ -15,6 +15,7 @@ #include +#include #include #include @@ -25,21 +26,76 @@ namespace applicationUtilities { namespace systemSoundPlayer { namespace test { +using namespace acsdkApplicationAudioPipelineFactoryInterfaces::test; +using namespace avsCommon::sdkInterfaces::audio; using namespace avsCommon::sdkInterfaces::audio::test; using namespace avsCommon::utils::mediaPlayer::test; using namespace ::testing; +/// Stub class that implements AudioFactoryInterface. +class StubAudioFactory : public AudioFactoryInterface { +public: + static std::shared_ptr createStubAudioFactory( + std::shared_ptr systemSoundFactory); + + std::shared_ptr alerts() const override; + std::shared_ptr notifications() const override; + std::shared_ptr communications() + const override; + std::shared_ptr systemSounds() const override; + +private: + StubAudioFactory(std::shared_ptr systemSoundFactory); + + std::shared_ptr m_systemSoundAudioFactory; +}; + +StubAudioFactory::StubAudioFactory(std::shared_ptr systemSoundFactory) : + m_systemSoundAudioFactory{systemSoundFactory} { +} + +std::shared_ptr StubAudioFactory::createStubAudioFactory( + std::shared_ptr systemSoundFactory) { + return std::shared_ptr(new StubAudioFactory(systemSoundFactory)); +} + +std::shared_ptr StubAudioFactory::alerts() const { + return nullptr; +} + +std::shared_ptr StubAudioFactory::notifications() + const { + return nullptr; +} + +std::shared_ptr StubAudioFactory::communications() + const { + return nullptr; +} + +std::shared_ptr StubAudioFactory::systemSounds() + const { + return m_systemSoundAudioFactory; +} + +/// SystemSoundPlayerTest unit tests. class SystemSoundPlayerTest : public ::testing::Test { public: void SetUp() override; void TearDown() override; /// @c SystemSoundPlayer to test - std::shared_ptr m_systemSoundPlayer; + std::shared_ptr m_systemSoundPlayer; /// Player to send the audio to. std::shared_ptr m_mockMediaPlayer; + /// Mock application audio pipeline factory. + std::shared_ptr m_mockAudioPipelineFactory; + + /// Factory to provide the mock system sound audio factory. + std::shared_ptr m_stubAudioFactory; + /// Factory to generate the system sound audio streams. std::shared_ptr m_mockSystemSoundAudioFactory; }; @@ -47,14 +103,63 @@ class SystemSoundPlayerTest : public ::testing::Test { void SystemSoundPlayerTest::SetUp() { m_mockSystemSoundAudioFactory = MockSystemSoundAudioFactory::create(); m_mockMediaPlayer = MockMediaPlayer::create(); + m_stubAudioFactory = StubAudioFactory::createStubAudioFactory(m_mockSystemSoundAudioFactory); + m_mockAudioPipelineFactory = std::make_shared>(); - m_systemSoundPlayer = SystemSoundPlayer::create(m_mockMediaPlayer, m_mockSystemSoundAudioFactory); + EXPECT_CALL( + *(m_mockAudioPipelineFactory.get()), + createApplicationMediaInterfaces(SYSTEM_SOUND_MEDIA_PLAYER_NAME, _, _, _, _, _)) + .WillRepeatedly(Return(std::make_shared( + m_mockMediaPlayer, nullptr, nullptr, nullptr, nullptr))); + + m_systemSoundPlayer = + SystemSoundPlayer::createSystemSoundPlayerInterface(m_mockAudioPipelineFactory, m_stubAudioFactory); } void SystemSoundPlayerTest::TearDown() { m_mockMediaPlayer->shutdown(); } +/** + * Test createSystemSoundPlayerInterface() simple failure cases + */ +TEST_F(SystemSoundPlayerTest, test_createSystemSoundPlayerInterfaceFailureCases) { + /// Expect failure with null audio pipeline factory. + auto testSystemSoundPlayer = SystemSoundPlayer::createSystemSoundPlayerInterface(nullptr, m_stubAudioFactory); + EXPECT_EQ(testSystemSoundPlayer, nullptr); + + /// Expect failure with null audio factory. + testSystemSoundPlayer = SystemSoundPlayer::createSystemSoundPlayerInterface(m_mockAudioPipelineFactory, nullptr); + EXPECT_EQ(testSystemSoundPlayer, nullptr); + + /// Expect failure when audio pipeline factory returns a nullptr for the application media interfaces. + auto failedAudioPipelineFactory = std::make_shared>(); + EXPECT_CALL( + *(failedAudioPipelineFactory.get()), + createApplicationMediaInterfaces(SYSTEM_SOUND_MEDIA_PLAYER_NAME, _, _, _, _, _)) + .WillOnce(Return(std::shared_ptr(nullptr))); + testSystemSoundPlayer = + SystemSoundPlayer::createSystemSoundPlayerInterface(failedAudioPipelineFactory, m_stubAudioFactory); + EXPECT_EQ(testSystemSoundPlayer, nullptr); + + /// Expect failure when application media interfaces contains a null media player. + failedAudioPipelineFactory = std::make_shared>(); + EXPECT_CALL( + *(failedAudioPipelineFactory.get()), + createApplicationMediaInterfaces(SYSTEM_SOUND_MEDIA_PLAYER_NAME, _, _, _, _, _)) + .WillOnce(Return(std::make_shared( + nullptr, nullptr, nullptr, nullptr, nullptr))); + testSystemSoundPlayer = + SystemSoundPlayer::createSystemSoundPlayerInterface(failedAudioPipelineFactory, m_stubAudioFactory); + EXPECT_EQ(testSystemSoundPlayer, nullptr); + + /// Expect failure when audio factory returns a nullptr for the system sounds audio factory. + auto failedAudioFactory = StubAudioFactory::createStubAudioFactory(nullptr); + testSystemSoundPlayer = + SystemSoundPlayer::createSystemSoundPlayerInterface(m_mockAudioPipelineFactory, failedAudioFactory); + EXPECT_EQ(testSystemSoundPlayer, nullptr); +} + /** * Test create() with nullptrs */ diff --git a/BluetoothImplementations/BlueZ/CMakeLists.txt b/BluetoothImplementations/BlueZ/CMakeLists.txt index a8a4cfd7cd..8927aeeb60 100644 --- a/BluetoothImplementations/BlueZ/CMakeLists.txt +++ b/BluetoothImplementations/BlueZ/CMakeLists.txt @@ -1,7 +1,5 @@ cmake_minimum_required(VERSION 3.1) project(BluetoothImplementationsBlueZ LANGUAGES CXX) -include(../../build/BuildDefaults.cmake) - add_subdirectory("src") add_subdirectory("test") diff --git a/BluetoothImplementations/BlueZ/include/BlueZ/BlueZBluetoothDeviceManager.h b/BluetoothImplementations/BlueZ/include/BlueZ/BlueZBluetoothDeviceManager.h index f2b82f1b2d..52fa6d6f9c 100644 --- a/BluetoothImplementations/BlueZ/include/BlueZ/BlueZBluetoothDeviceManager.h +++ b/BluetoothImplementations/BlueZ/include/BlueZ/BlueZBluetoothDeviceManager.h @@ -26,14 +26,24 @@ namespace bluetoothImplementations { namespace blueZ { /** - * BlueZ simplementation of @c BluetoothDeviceManagerInterface. This class is required to allow only one instance + * BlueZ implementation of @c BluetoothDeviceManagerInterface. This class is required to allow only one instance * of @c BluetoothDeviceManagerInterface in the SDK. */ class BlueZBluetoothDeviceManager : public avsCommon::sdkInterfaces::bluetooth::BluetoothDeviceManagerInterface { public: + /** + * Factory method to create a @c BluetoothDeviceManagerInterface. + * + * @param eventBus @c BluetoothEvent typed @c EventBus to be used for event routing. + * @return A unique pointer to a new @c BluetoothDeviceManagerInterface on success, nullptr otherwise. + */ + static std::shared_ptr + createBluetoothDeviceManagerInterface(std::shared_ptr eventBus); + /** * Factory method to create a class. * + * @deprecated * @param eventBus @c BluethoothEvent typed @c EventBus to be used for event routing. * @return A new instance of BlueZBluetoothDeviceManager on success, nullptr otherwise. */ diff --git a/BluetoothImplementations/BlueZ/include/BlueZ/PulseAudioBluetoothInitializer.h b/BluetoothImplementations/BlueZ/include/BlueZ/PulseAudioBluetoothInitializer.h index 9605eeda76..24ef389436 100644 --- a/BluetoothImplementations/BlueZ/include/BlueZ/PulseAudioBluetoothInitializer.h +++ b/BluetoothImplementations/BlueZ/include/BlueZ/PulseAudioBluetoothInitializer.h @@ -270,4 +270,4 @@ inline std::ostream& operator<<(std::ostream& stream, const PulseAudioBluetoothI } // namespace bluetoothImplementations } // namespace alexaClientSDK -#endif // ALEXA_CLIENT_SDK_BLUETOOTHIMPLEMENTATIONS_BLUEZ_INCLUDE_BLUEZ_PULSEAUDIOBLUETOOTHINITIALIZER_H_ +#endif // ALEXA_CLIENT_SDK_BLUETOOTHIMPLEMENTATIONS_BLUEZ_INCLUDE_BLUEZ_PULSEAUDIOBLUETOOTHINITIALIZER_H_ \ No newline at end of file diff --git a/BluetoothImplementations/BlueZ/src/BlueZBluetoothDeviceManager.cpp b/BluetoothImplementations/BlueZ/src/BlueZBluetoothDeviceManager.cpp index cbbe67d7dc..e10d5a7eef 100644 --- a/BluetoothImplementations/BlueZ/src/BlueZBluetoothDeviceManager.cpp +++ b/BluetoothImplementations/BlueZ/src/BlueZBluetoothDeviceManager.cpp @@ -42,6 +42,12 @@ std::list> BlueZBluetoothDeviceManager return m_deviceManager->getDiscoveredDevices(); } +std::shared_ptr BlueZBluetoothDeviceManager::createBluetoothDeviceManagerInterface( + std::shared_ptr eventBus) { + auto bluetooth = create(eventBus); + return std::move(bluetooth); +} + std::unique_ptr BlueZBluetoothDeviceManager::create( std::shared_ptr eventBus) { auto deviceManager = BlueZDeviceManager::create(eventBus); diff --git a/BluetoothImplementations/BlueZ/src/DBusConnection.cpp b/BluetoothImplementations/BlueZ/src/DBusConnection.cpp index 91932caa12..51296ea3e7 100644 --- a/BluetoothImplementations/BlueZ/src/DBusConnection.cpp +++ b/BluetoothImplementations/BlueZ/src/DBusConnection.cpp @@ -131,7 +131,6 @@ void DBusConnection::close() { } DBusConnection::~DBusConnection() { - ACSDK_DEBUG7(LX(__func__)); close(); } diff --git a/BluetoothImplementations/BlueZ/src/PulseAudioBluetoothInitializer.cpp b/BluetoothImplementations/BlueZ/src/PulseAudioBluetoothInitializer.cpp index 8e112480f2..1812af5e8b 100644 --- a/BluetoothImplementations/BlueZ/src/PulseAudioBluetoothInitializer.cpp +++ b/BluetoothImplementations/BlueZ/src/PulseAudioBluetoothInitializer.cpp @@ -291,12 +291,12 @@ void PulseAudioBluetoothInitializer::setStateAndNotify(pa_context_state_t state) // Connected and ready to receive calls. case PA_CONTEXT_READY: m_connected = true; - // These are failed cases. + // These are failed cases. case PA_CONTEXT_FAILED: case PA_CONTEXT_TERMINATED: m_mainThreadCv.notify_one(); break; - // Intermediate states that can be ignored. + // Intermediate states that can be ignored. case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: diff --git a/BluetoothImplementations/BlueZ/test/MPRISPlayerTest.cpp b/BluetoothImplementations/BlueZ/test/MPRISPlayerTest.cpp index 455e64e572..abb19e60d9 100644 --- a/BluetoothImplementations/BlueZ/test/MPRISPlayerTest.cpp +++ b/BluetoothImplementations/BlueZ/test/MPRISPlayerTest.cpp @@ -39,7 +39,7 @@ using namespace avsCommon::utils::bluetooth; * * @c G_BUS_TYPE_SESSION is being used because @c G_BUS_TYPE_SYSTEM has stricter permissions. */ -static std::shared_ptr g_connection = DBusConnection::create(G_BUS_TYPE_SESSION); +static std::shared_ptr g_connection; /// Unique bus name to allow DBus method invocations. static const std::string UNIQUE_BUS = "org.avscppsdk.test.unique"; @@ -53,6 +53,24 @@ class MockListener : public BluetoothEventListenerInterface { MOCK_METHOD1(onEventFired, void(const BluetoothEvent& event)); }; +/// Class used to properly initialize and tear down g_connection. +class Environment : public testing::Environment { +public: + /// Test Program SetUp. + void SetUp() override; + + /// Test Program TearDown. + void TearDown() override; +}; + +void Environment::SetUp() { + g_connection = DBusConnection::create(G_BUS_TYPE_SESSION); +} + +void Environment::TearDown() { + g_connection.reset(); +} + /** * Test fixture for the base tests. DBus objects are not mockable, so we use live calls to DBus for these tests. */ @@ -263,3 +281,14 @@ TEST_P(MPRISPlayerUnsupportedTest, test_unsupportedMethod) { } // namespace blueZ } // namespace bluetoothImplementations } // namespace alexaClientSDK + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + alexaClientSDK::bluetoothImplementations::blueZ::test::Environment* env = + new alexaClientSDK::bluetoothImplementations::blueZ::test::Environment(); + + // gmock owns the lifecyle of any registered environment, there is no need to free. + AddGlobalTestEnvironment(env); + return RUN_ALL_TESTS(); +} diff --git a/BluetoothImplementations/CMakeLists.txt b/BluetoothImplementations/CMakeLists.txt index f517b3b734..e75626e971 100644 --- a/BluetoothImplementations/CMakeLists.txt +++ b/BluetoothImplementations/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1) project(BluetoothImplementations LANGUAGES CXX) -include(../build/BuildDefaults.cmake) +include("${AVS_CMAKE_BUILD}/BuildDefaults.cmake") if(BLUETOOTH_BLUEZ) add_subdirectory("BlueZ") diff --git a/CHANGELOG.md b/CHANGELOG.md index 020a057b48..9afbafbcf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## ChangeLog +### Version 1.23.0 - March 29 2021 +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.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) diff --git a/CMakeLists.txt b/CMakeLists.txt index dde4782046..f95a5ff02f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,17 +1,20 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) # Set project information -project(AlexaClientSDK VERSION 1.22.0 LANGUAGES CXX) +project(AlexaClientSDK VERSION 1.23.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. get_filename_component(AVS_CORE . ABSOLUTE) -include(build/BuildDefaults.cmake) +# This variable should be used to get the cmake build files. +get_filename_component(AVS_CMAKE_BUILD cmakeBuild ABSOLUTE) + +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) include(tools/Testing.cmake) # Set variables for target install and .pc pkg-config file -include(build/cmake/PrepareInstall.cmake) +include(${AVS_CMAKE_BUILD}/cmake/PrepareInstall.cmake) configure_file ( "${PROJECT_SOURCE_DIR}/AVSCommon/Utils/include/AVSCommon/Utils/SDKVersion.h.in" @@ -19,7 +22,7 @@ configure_file ( ) # Add extension point targets. -include(build/cmake/ExtensionPath.cmake) +include(${AVS_CMAKE_BUILD}/cmake/ExtensionPath.cmake) add_extension_projects() # Alexa Client SDK targets. @@ -60,4 +63,4 @@ add_subdirectory("SynchronizeStateSender") add_subdirectory("doc") # Create .pc pkg-config file -include(build/cmake/GeneratePkgConfig.cmake) +include(${AVS_CMAKE_BUILD}/cmake/GeneratePkgConfig.cmake) diff --git a/CapabilitiesDelegate/CMakeLists.txt b/CapabilitiesDelegate/CMakeLists.txt index 8833fcae33..18fe9cf998 100644 --- a/CapabilitiesDelegate/CMakeLists.txt +++ b/CapabilitiesDelegate/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(CapabilitiesDelegate LANGUAGES CXX) -include(../build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/CapabilitiesDelegate/include/CapabilitiesDelegate/CapabilitiesDelegate.h b/CapabilitiesDelegate/include/CapabilitiesDelegate/CapabilitiesDelegate.h index 7368faf1d7..1b5017f2ba 100644 --- a/CapabilitiesDelegate/include/CapabilitiesDelegate/CapabilitiesDelegate.h +++ b/CapabilitiesDelegate/include/CapabilitiesDelegate/CapabilitiesDelegate.h @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include @@ -116,10 +116,10 @@ class CapabilitiesDelegate const std::vector& capabilities) override; void addCapabilitiesObserver( - std::shared_ptr observer) override; + std::shared_ptr observer) override; void removeCapabilitiesObserver( - std::shared_ptr observer) override; + std::shared_ptr observer) override; void invalidateCapabilities() override; @@ -210,8 +210,8 @@ class CapabilitiesDelegate * deletion. */ void setCapabilitiesState( - const avsCommon::sdkInterfaces::CapabilitiesObserverInterface::State newState, - const avsCommon::sdkInterfaces::CapabilitiesObserverInterface::Error newError, + const avsCommon::sdkInterfaces::CapabilitiesDelegateObserverInterface::State newState, + const avsCommon::sdkInterfaces::CapabilitiesDelegateObserverInterface::Error newError, const std::vector& addOrUpdateReportEndpoints, const std::vector& deleteReportEndpoints); @@ -289,14 +289,14 @@ class CapabilitiesDelegate std::mutex m_observerMutex; /// Authorization state change observers. Access is synchronized with @c m_capabilitiesMutex. - std::unordered_set> + std::unordered_set> m_capabilitiesObservers; /// Current state of CapabilitiesDelegate. Access is synchronized with @c m_capabilitiesMutex. - avsCommon::sdkInterfaces::CapabilitiesObserverInterface::State m_capabilitiesState; + avsCommon::sdkInterfaces::CapabilitiesDelegateObserverInterface::State m_capabilitiesState; /// Current CapabilitiesDelegate error. Access is synchronized with @c m_capabilitiesMutex. - avsCommon::sdkInterfaces::CapabilitiesObserverInterface::Error m_capabilitiesError; + avsCommon::sdkInterfaces::CapabilitiesDelegateObserverInterface::Error m_capabilitiesError; /// Auth delegate used to get the access token std::shared_ptr m_authDelegate; diff --git a/CapabilitiesDelegate/src/CapabilitiesDelegate.cpp b/CapabilitiesDelegate/src/CapabilitiesDelegate.cpp index 6e8e726c66..895ba63716 100644 --- a/CapabilitiesDelegate/src/CapabilitiesDelegate.cpp +++ b/CapabilitiesDelegate/src/CapabilitiesDelegate.cpp @@ -127,8 +127,8 @@ CapabilitiesDelegate::CapabilitiesDelegate( const std::shared_ptr& customerDataManager) : RequiresShutdown{"CapabilitiesDelegate"}, CustomerDataHandler{customerDataManager}, - m_capabilitiesState{CapabilitiesObserverInterface::State::UNINITIALIZED}, - m_capabilitiesError{CapabilitiesObserverInterface::Error::UNINITIALIZED}, + m_capabilitiesState{CapabilitiesDelegateObserverInterface::State::UNINITIALIZED}, + m_capabilitiesError{CapabilitiesDelegateObserverInterface::Error::UNINITIALIZED}, m_authDelegate{authDelegate}, m_capabilitiesDelegateStorage{capabilitiesDelegateStorage}, m_isConnected{false}, @@ -170,7 +170,7 @@ bool CapabilitiesDelegate::init() { return true; } -void CapabilitiesDelegate::addCapabilitiesObserver(std::shared_ptr observer) { +void CapabilitiesDelegate::addCapabilitiesObserver(std::shared_ptr observer) { if (!observer) { ACSDK_ERROR(LX("addCapabilitiesObserverFailed").d("reason", "nullObserver")); return; @@ -190,7 +190,7 @@ void CapabilitiesDelegate::addCapabilitiesObserver(std::shared_ptr{}, std::vector{}); } -void CapabilitiesDelegate::removeCapabilitiesObserver(std::shared_ptr observer) { +void CapabilitiesDelegate::removeCapabilitiesObserver(std::shared_ptr observer) { if (!observer) { ACSDK_ERROR(LX("removeCapabilitiesObserverFailed").d("reason", "nullObserver")); return; @@ -205,13 +205,14 @@ void CapabilitiesDelegate::removeCapabilitiesObserver(std::shared_ptr& addOrUpdateReportEndpoints, const std::vector& deleteReportEndpoints) { ACSDK_DEBUG5(LX("setCapabilitiesState").d("newCapabilitiesState", newCapabilitiesState)); - std::unordered_set> capabilitiesObservers; + std::unordered_set> + capabilitiesObservers; { std::lock_guard lock(m_observerMutex); capabilitiesObservers = m_capabilitiesObservers; @@ -425,8 +426,8 @@ void CapabilitiesDelegate::executeSendPendingEndpoints() { ACSDK_ERROR(LX("failedExecuteSendPendingEndpoints").d("reason", "failed to create DiscoveryEventSender")); moveInFlightEndpointsToPending(); setCapabilitiesState( - CapabilitiesObserverInterface::State::FATAL_ERROR, - CapabilitiesObserverInterface::Error::UNKNOWN_ERROR, + CapabilitiesDelegateObserverInterface::State::FATAL_ERROR, + CapabilitiesDelegateObserverInterface::Error::UNKNOWN_ERROR, getEndpointIdentifiers(addOrUpdateEndpointsToSend), getEndpointIdentifiers(deleteEndpointsToSend)); return; @@ -521,8 +522,8 @@ std::shared_ptr CapabilitiesDelegate::createPostC /// CapabilitiesDelegate. if (addOrUpdateEndpointsToSend.empty() && !originalPendingAddOrUpdateEndpoints.empty()) { setCapabilitiesState( - CapabilitiesObserverInterface::State::SUCCESS, - CapabilitiesObserverInterface::Error::SUCCESS, + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, getEndpointIdentifiers(originalPendingAddOrUpdateEndpoints), std::vector{}); } @@ -572,8 +573,8 @@ void CapabilitiesDelegate::onDiscoveryCompleted( if (!updateEndpointConfigInStorage(addOrUpdateReportEndpoints, deleteReportEndpoints)) { ACSDK_ERROR(LX("publishCapabilitiesFailed").d("reason", "failed to save endpointConfig to database")); setCapabilitiesState( - CapabilitiesObserverInterface::State::FATAL_ERROR, - CapabilitiesObserverInterface::Error::UNKNOWN_ERROR, + CapabilitiesDelegateObserverInterface::State::FATAL_ERROR, + CapabilitiesDelegateObserverInterface::Error::UNKNOWN_ERROR, addOrUpdateReportEndpointIdentifiers, deleteReportEndpointIdentifiers); return; @@ -583,8 +584,8 @@ void CapabilitiesDelegate::onDiscoveryCompleted( resetCurrentDiscoveryEventSender(); setCapabilitiesState( - CapabilitiesObserverInterface::State::SUCCESS, - CapabilitiesObserverInterface::Error::SUCCESS, + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, addOrUpdateReportEndpointIdentifiers, deleteReportEndpointIdentifiers); @@ -610,8 +611,8 @@ void CapabilitiesDelegate::onDiscoveryFailure(MessageRequestObserverInterface::S resetCurrentDiscoveryEventSender(); setCapabilitiesState( - CapabilitiesObserverInterface::State::FATAL_ERROR, - CapabilitiesObserverInterface::Error::FORBIDDEN, + CapabilitiesDelegateObserverInterface::State::FATAL_ERROR, + CapabilitiesDelegateObserverInterface::Error::FORBIDDEN, addOrUpdateReportEndpointIdentifiers, deleteReportEndpointIdentifiers); break; @@ -621,8 +622,8 @@ void CapabilitiesDelegate::onDiscoveryFailure(MessageRequestObserverInterface::S resetCurrentDiscoveryEventSender(); setCapabilitiesState( - CapabilitiesObserverInterface::State::FATAL_ERROR, - CapabilitiesObserverInterface::Error::BAD_REQUEST, + CapabilitiesDelegateObserverInterface::State::FATAL_ERROR, + CapabilitiesDelegateObserverInterface::Error::BAD_REQUEST, addOrUpdateReportEndpointIdentifiers, deleteReportEndpointIdentifiers); break; @@ -632,8 +633,8 @@ void CapabilitiesDelegate::onDiscoveryFailure(MessageRequestObserverInterface::S } setCapabilitiesState( - CapabilitiesObserverInterface::State::RETRIABLE_ERROR, - CapabilitiesObserverInterface::Error::SERVER_INTERNAL_ERROR, + CapabilitiesDelegateObserverInterface::State::RETRIABLE_ERROR, + CapabilitiesDelegateObserverInterface::Error::SERVER_INTERNAL_ERROR, addOrUpdateReportEndpointIdentifiers, deleteReportEndpointIdentifiers); break; @@ -643,8 +644,8 @@ void CapabilitiesDelegate::onDiscoveryFailure(MessageRequestObserverInterface::S } setCapabilitiesState( - CapabilitiesObserverInterface::State::RETRIABLE_ERROR, - CapabilitiesObserverInterface::Error::UNKNOWN_ERROR, + CapabilitiesDelegateObserverInterface::State::RETRIABLE_ERROR, + CapabilitiesDelegateObserverInterface::Error::UNKNOWN_ERROR, addOrUpdateReportEndpointIdentifiers, deleteReportEndpointIdentifiers); break; diff --git a/CapabilitiesDelegate/test/CapabilitiesDelegateTest.cpp b/CapabilitiesDelegate/test/CapabilitiesDelegateTest.cpp index 25e750b330..3c8509295b 100644 --- a/CapabilitiesDelegate/test/CapabilitiesDelegateTest.cpp +++ b/CapabilitiesDelegate/test/CapabilitiesDelegateTest.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include #include @@ -28,7 +28,7 @@ #include #include "MockAuthDelegate.h" -#include "MockCapabilitiesObserver.h" +#include "MockCapabilitiesDelegateObserver.h" #include "MockCapabilitiesStorage.h" #include "MockDiscoveryEventSender.h" @@ -138,8 +138,8 @@ class CapabilitiesDelegateTest : public Test { /// The mock Capabilities Storage instance. std::shared_ptr m_mockCapabilitiesStorage; - /// The mock Capabilities observer instance. - std::shared_ptr m_mockCapabilitiesObserver; + /// The mock CapabilitiesDelegate observer instance. + std::shared_ptr m_mockCapabilitiesDelegateObserver; /// The data manager required to build the base object std::shared_ptr m_dataManager; @@ -154,7 +154,7 @@ class CapabilitiesDelegateTest : public Test { void CapabilitiesDelegateTest::SetUp() { m_mockCapabilitiesStorage = std::make_shared>(); m_mockAuthDelegate = std::make_shared>(); - m_mockCapabilitiesObserver = std::make_shared>(); + m_mockCapabilitiesDelegateObserver = std::make_shared>(); m_dataManager = std::make_shared(); m_mockMessageSender = std::make_shared>(); @@ -164,19 +164,19 @@ void CapabilitiesDelegateTest::SetUp() { ASSERT_NE(m_capabilitiesDelegate, nullptr); /// Add a new observer and it receives notifications of the current capabilities state. - m_mockCapabilitiesObserver = std::make_shared>(); - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([](CapabilitiesObserverInterface::State newState, - CapabilitiesObserverInterface::Error newError, + m_mockCapabilitiesDelegateObserver = std::make_shared>(); + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([](CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, std::vector addOrUpdateReportEndpointIdentifiers, std::vector deleteReportEndpointIdentifiers) { - EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED); - EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED); + EXPECT_EQ(newState, CapabilitiesDelegateObserverInterface::State::UNINITIALIZED); + EXPECT_EQ(newError, CapabilitiesDelegateObserverInterface::Error::UNINITIALIZED); EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector{}); EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector{}); })); - m_capabilitiesDelegate->addCapabilitiesObserver(m_mockCapabilitiesObserver); + m_capabilitiesDelegate->addCapabilitiesObserver(m_mockCapabilitiesDelegateObserver); m_capabilitiesDelegate->setMessageSender(m_mockMessageSender); } @@ -229,12 +229,15 @@ void CapabilitiesDelegateTest::addEndpoint( .WillOnce(Return(true)); EXPECT_CALL( - *m_mockCapabilitiesObserver, + *m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, CapabilitiesObserverInterface::Error::SUCCESS, _, _)) + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, + _, + _)) .Times(1) - .WillOnce(Invoke([&](CapabilitiesObserverInterface::State state, - CapabilitiesObserverInterface::Error error, + .WillOnce(Invoke([&](CapabilitiesDelegateObserverInterface::State state, + CapabilitiesDelegateObserverInterface::Error error, std::vector addedEndpoints, std::vector deletedEndpoints) { e.wakeUp(); })); @@ -327,15 +330,15 @@ TEST_F(CapabilitiesDelegateTest, test_addCapabilitiesObserver) { m_capabilitiesDelegate->addCapabilitiesObserver(nullptr); /// Add a new observer and it receives notifications of the current capabilities state. - auto mockObserver = std::make_shared>(); + auto mockObserver = std::make_shared>(); EXPECT_CALL(*mockObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([](CapabilitiesObserverInterface::State newState, - CapabilitiesObserverInterface::Error newError, + .WillOnce(Invoke([](CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, std::vector addOrUpdateReportEndpointIdentifiers, std::vector deleteReportEndpointIdentifiers) { - EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED); - EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED); + EXPECT_EQ(newState, CapabilitiesDelegateObserverInterface::State::UNINITIALIZED); + EXPECT_EQ(newError, CapabilitiesDelegateObserverInterface::Error::UNINITIALIZED); EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector{}); EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector{}); })); @@ -355,13 +358,13 @@ TEST_F(CapabilitiesDelegateTest, test_onDiscoveryCompleted) { EXPECT_CALL(*m_mockCapabilitiesStorage, store(addOrUpdateReportEndpoints)).WillOnce(Return(true)); EXPECT_CALL(*m_mockCapabilitiesStorage, erase(deleteReportEndpoints)).WillOnce(Return(true)); - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([](CapabilitiesObserverInterface::State newState, - CapabilitiesObserverInterface::Error newError, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([](CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, std::vector addOrUpdateReportEndpointIdentifiers, std::vector deleteReportEndpointIdentifiers) { - EXPECT_EQ(newState, CapabilitiesObserverInterface::State::SUCCESS); - EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::SUCCESS); + EXPECT_EQ(newState, CapabilitiesDelegateObserverInterface::State::SUCCESS); + EXPECT_EQ(newError, CapabilitiesDelegateObserverInterface::Error::SUCCESS); std::sort(addOrUpdateReportEndpointIdentifiers.begin(), addOrUpdateReportEndpointIdentifiers.end()); EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, (std::vector{"add_1", "update_1"})); @@ -372,7 +375,7 @@ TEST_F(CapabilitiesDelegateTest, test_onDiscoveryCompleted) { m_capabilitiesDelegate->onDiscoveryCompleted(addOrUpdateReportEndpoints, deleteReportEndpoints); /// Check removing observer does not send notifications to the observer. - m_capabilitiesDelegate->removeCapabilitiesObserver(m_mockCapabilitiesObserver); + m_capabilitiesDelegate->removeCapabilitiesObserver(m_mockCapabilitiesDelegateObserver); EXPECT_CALL(*m_mockCapabilitiesStorage, store(addOrUpdateReportEndpoints)).WillOnce(Return(true)); EXPECT_CALL(*m_mockCapabilitiesStorage, erase(deleteReportEndpoints)).WillOnce(Return(true)); @@ -391,13 +394,13 @@ TEST_F(CapabilitiesDelegateTest, test_onDiscoveryCompletedButStorageFails) { EXPECT_CALL(*m_mockCapabilitiesStorage, store(addOrUpdateReportEndpoints)).WillOnce(Return(false)); - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([](CapabilitiesObserverInterface::State newState, - CapabilitiesObserverInterface::Error newError, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([](CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, std::vector addOrUpdateReportEndpointIdentifiers, std::vector deleteReportEndpointIdentifiers) { - EXPECT_EQ(newState, CapabilitiesObserverInterface::State::FATAL_ERROR); - EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNKNOWN_ERROR); + EXPECT_EQ(newState, CapabilitiesDelegateObserverInterface::State::FATAL_ERROR); + EXPECT_EQ(newError, CapabilitiesDelegateObserverInterface::Error::UNKNOWN_ERROR); std::sort(addOrUpdateReportEndpointIdentifiers.begin(), addOrUpdateReportEndpointIdentifiers.end()); EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, (std::vector{"add_1", "update_1"})); @@ -413,13 +416,13 @@ TEST_F(CapabilitiesDelegateTest, test_onDiscoveryCompletedButStorageFails) { */ TEST_F(CapabilitiesDelegateTest, test_onDiscoveryFailure) { /// validate retriable error response - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([](CapabilitiesObserverInterface::State newState, - CapabilitiesObserverInterface::Error newError, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([](CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, std::vector addOrUpdateReportEndpointIdentifiers, std::vector deleteReportEndpointIdentifiers) { - EXPECT_EQ(newState, CapabilitiesObserverInterface::State::RETRIABLE_ERROR); - EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::SERVER_INTERNAL_ERROR); + EXPECT_EQ(newState, CapabilitiesDelegateObserverInterface::State::RETRIABLE_ERROR); + EXPECT_EQ(newError, CapabilitiesDelegateObserverInterface::Error::SERVER_INTERNAL_ERROR); EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector{}); EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector{}); })); @@ -427,13 +430,13 @@ TEST_F(CapabilitiesDelegateTest, test_onDiscoveryFailure) { m_capabilitiesDelegate->onDiscoveryFailure(MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2); /// validate invalid auth error response - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([](CapabilitiesObserverInterface::State newState, - CapabilitiesObserverInterface::Error newError, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([](CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, std::vector addOrUpdateReportEndpointIdentifiers, std::vector deleteReportEndpointIdentifiers) { - EXPECT_EQ(newState, CapabilitiesObserverInterface::State::FATAL_ERROR); - EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::FORBIDDEN); + EXPECT_EQ(newState, CapabilitiesDelegateObserverInterface::State::FATAL_ERROR); + EXPECT_EQ(newError, CapabilitiesDelegateObserverInterface::Error::FORBIDDEN); EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector{}); EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector{}); })); @@ -441,13 +444,13 @@ TEST_F(CapabilitiesDelegateTest, test_onDiscoveryFailure) { m_capabilitiesDelegate->onDiscoveryFailure(MessageRequestObserverInterface::Status::INVALID_AUTH); /// validate bad request error response - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([](CapabilitiesObserverInterface::State newState, - CapabilitiesObserverInterface::Error newError, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([](CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, std::vector addOrUpdateReportEndpointIdentifiers, std::vector deleteReportEndpointIdentifiers) { - EXPECT_EQ(newState, CapabilitiesObserverInterface::State::FATAL_ERROR); - EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::BAD_REQUEST); + EXPECT_EQ(newState, CapabilitiesDelegateObserverInterface::State::FATAL_ERROR); + EXPECT_EQ(newError, CapabilitiesDelegateObserverInterface::Error::BAD_REQUEST); EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector{}); EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector{}); })); @@ -455,13 +458,13 @@ TEST_F(CapabilitiesDelegateTest, test_onDiscoveryFailure) { m_capabilitiesDelegate->onDiscoveryFailure(MessageRequestObserverInterface::Status::BAD_REQUEST); /// other responses - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([](CapabilitiesObserverInterface::State newState, - CapabilitiesObserverInterface::Error newError, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([](CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, std::vector addOrUpdateReportEndpointIdentifiers, std::vector deleteReportEndpointIdentifiers) { - EXPECT_EQ(newState, CapabilitiesObserverInterface::State::RETRIABLE_ERROR); - EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNKNOWN_ERROR); + EXPECT_EQ(newState, CapabilitiesDelegateObserverInterface::State::RETRIABLE_ERROR); + EXPECT_EQ(newError, CapabilitiesDelegateObserverInterface::Error::UNKNOWN_ERROR); EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector{}); EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector{}); })); @@ -533,13 +536,13 @@ TEST_F(CapabilitiesDelegateTest, test_dynamicAddOrUpdateEndpoint) { .WillOnce(Return(true)); /// Expect callback to CapabilitiesObserver. - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([&](CapabilitiesObserverInterface::State state, - CapabilitiesObserverInterface::Error error, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([&](CapabilitiesDelegateObserverInterface::State state, + CapabilitiesDelegateObserverInterface::Error error, std::vector addedEndpoints, std::vector deletedEndpoints) { - EXPECT_EQ(state, CapabilitiesObserverInterface::State::SUCCESS); - EXPECT_EQ(error, CapabilitiesObserverInterface::Error::SUCCESS); + EXPECT_EQ(state, CapabilitiesDelegateObserverInterface::State::SUCCESS); + EXPECT_EQ(error, CapabilitiesDelegateObserverInterface::Error::SUCCESS); EXPECT_EQ(addedEndpoints, (std::vector{"endpointId"})); EXPECT_EQ(deletedEndpoints, (std::vector{})); @@ -605,13 +608,13 @@ TEST_F(CapabilitiesDelegateTest, test_dynamicDeleteEndpoint) { .WillOnce(Return(true)); /// Expect callback to CapabilitiesObserver. - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([&](CapabilitiesObserverInterface::State state, - CapabilitiesObserverInterface::Error error, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([&](CapabilitiesDelegateObserverInterface::State state, + CapabilitiesDelegateObserverInterface::Error error, std::vector addedEndpoints, std::vector deletedEndpoints) { - EXPECT_EQ(state, CapabilitiesObserverInterface::State::SUCCESS); - EXPECT_EQ(error, CapabilitiesObserverInterface::Error::SUCCESS); + EXPECT_EQ(state, CapabilitiesDelegateObserverInterface::State::SUCCESS); + EXPECT_EQ(error, CapabilitiesDelegateObserverInterface::Error::SUCCESS); EXPECT_EQ(addedEndpoints, (std::vector{})); EXPECT_EQ(deletedEndpoints, (std::vector{"deleteId"})); e.wakeUp(); @@ -681,29 +684,29 @@ TEST_F(CapabilitiesDelegateTest, test_createPostConnectOperationWithPendingEndpo storedEndpoints->insert({endpointAttributes.endpointId, endpointConfig}); return true; })); - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([](CapabilitiesObserverInterface::State newState, - CapabilitiesObserverInterface::Error newError, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([](CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, std::vector addOrUpdateReportEndpointIdentifiers, std::vector deleteReportEndpointIdentifiers) { - EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED); - EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED); + EXPECT_EQ(newState, CapabilitiesDelegateObserverInterface::State::UNINITIALIZED); + EXPECT_EQ(newError, CapabilitiesDelegateObserverInterface::Error::UNINITIALIZED); EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector{}); EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector{}); })); auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); - instance->addCapabilitiesObserver(m_mockCapabilitiesObserver); + instance->addCapabilitiesObserver(m_mockCapabilitiesDelegateObserver); instance->addOrUpdateEndpoint(endpointAttributes, capabilityConfigs); /// Endpoint config is same as the endpoint config created with the test endpoint attributes so a /// post connect operation is not created. However, we do expect an observer callback as there were pending /// endpoints. EXPECT_CALL( - *m_mockCapabilitiesObserver, + *m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, - CapabilitiesObserverInterface::Error::SUCCESS, + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, std::vector{endpointAttributes.endpointId}, _)); auto publisher = instance->createPostConnectOperation(); @@ -735,29 +738,29 @@ TEST_F(CapabilitiesDelegateTest, test_createPostConnectOperationWithoutPendingEn storedEndpoints->insert({endpointAttributes.endpointId, endpointConfig}); return true; })); - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([](CapabilitiesObserverInterface::State newState, - CapabilitiesObserverInterface::Error newError, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([](CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, std::vector addOrUpdateReportEndpointIdentifiers, std::vector deleteReportEndpointIdentifiers) { - EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED); - EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED); + EXPECT_EQ(newState, CapabilitiesDelegateObserverInterface::State::UNINITIALIZED); + EXPECT_EQ(newError, CapabilitiesDelegateObserverInterface::Error::UNINITIALIZED); EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector{}); EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector{}); })); auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); - instance->addCapabilitiesObserver(m_mockCapabilitiesObserver); + instance->addCapabilitiesObserver(m_mockCapabilitiesDelegateObserver); /// Add the endpoint here before creating the initial post-connect, which forces the endpoint to be cached in /// CapabilitiesDelegate. /// Here we do expect an observer callback because there is a pending endpoint. This is test set-up.s instance->addOrUpdateEndpoint(endpointAttributes, capabilityConfigs); EXPECT_CALL( - *m_mockCapabilitiesObserver, + *m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, - CapabilitiesObserverInterface::Error::SUCCESS, + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, std::vector{endpointAttributes.endpointId}, _)); auto publisher = instance->createPostConnectOperation(); @@ -805,19 +808,19 @@ TEST_F(CapabilitiesDelegateTest, test_createPostConnectOperationCachesEndpoints) /// time, we can verify that it creates a non-null post-connect publisher to send the cached endpoint. return true; })); - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([](CapabilitiesObserverInterface::State newState, - CapabilitiesObserverInterface::Error newError, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([](CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, std::vector addOrUpdateReportEndpointIdentifiers, std::vector deleteReportEndpointIdentifiers) { - EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED); - EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED); + EXPECT_EQ(newState, CapabilitiesDelegateObserverInterface::State::UNINITIALIZED); + EXPECT_EQ(newError, CapabilitiesDelegateObserverInterface::Error::UNINITIALIZED); EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector{}); EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector{}); })); auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); - instance->addCapabilitiesObserver(m_mockCapabilitiesObserver); + instance->addCapabilitiesObserver(m_mockCapabilitiesDelegateObserver); /// Endpoint config is same as the endpoint config created with the test endpoint attributes so a /// post connect operation is not created, but we add the endpoint here so that it is cached in CapabilitiesDelegate @@ -825,10 +828,10 @@ TEST_F(CapabilitiesDelegateTest, test_createPostConnectOperationCachesEndpoints) /// Expect an observer callback here, since there is a pending endpoint. instance->addOrUpdateEndpoint(endpointAttributes, capabilityConfigs); EXPECT_CALL( - *m_mockCapabilitiesObserver, + *m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, - CapabilitiesObserverInterface::Error::SUCCESS, + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, std::vector{endpointAttributes.endpointId}, _)); auto publisher = instance->createPostConnectOperation(); @@ -882,28 +885,28 @@ TEST_F( storedEndpoints->insert({staleEndpointAttributes.endpointId, staleEndpointConfig}); return true; })); - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([](CapabilitiesObserverInterface::State newState, - CapabilitiesObserverInterface::Error newError, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([](CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, std::vector addOrUpdateReportEndpointIdentifiers, std::vector deleteReportEndpointIdentifiers) { - EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED); - EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED); + EXPECT_EQ(newState, CapabilitiesDelegateObserverInterface::State::UNINITIALIZED); + EXPECT_EQ(newError, CapabilitiesDelegateObserverInterface::Error::UNINITIALIZED); EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector{}); EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector{}); })); auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); - instance->addCapabilitiesObserver(m_mockCapabilitiesObserver); + instance->addCapabilitiesObserver(m_mockCapabilitiesDelegateObserver); /// Endpoint config is same as the endpoint config created with the test endpoint attributes so a /// post connect operation is not created, but add it here so that it is cached in CapabilitiesDelegate. instance->addOrUpdateEndpoint(endpointAttributes, capabilityConfigs); EXPECT_CALL( - *m_mockCapabilitiesObserver, + *m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, - CapabilitiesObserverInterface::Error::SUCCESS, + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, std::vector{endpointAttributes.endpointId}, _)); auto publisher = instance->createPostConnectOperation(); @@ -940,19 +943,19 @@ TEST_F(CapabilitiesDelegateTest, test_createPostConnectOperationWithStaleEndpoin storedEndpoints->insert({staleEndpointAttributes.endpointId, staleEndpointConfig}); return true; })); - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([](CapabilitiesObserverInterface::State newState, - CapabilitiesObserverInterface::Error newError, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([](CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, std::vector addOrUpdateReportEndpointIdentifiers, std::vector deleteReportEndpointIdentifiers) { - EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED); - EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED); + EXPECT_EQ(newState, CapabilitiesDelegateObserverInterface::State::UNINITIALIZED); + EXPECT_EQ(newError, CapabilitiesDelegateObserverInterface::Error::UNINITIALIZED); EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector{}); EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector{}); })); auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); - instance->addCapabilitiesObserver(m_mockCapabilitiesObserver); + instance->addCapabilitiesObserver(m_mockCapabilitiesDelegateObserver); /// There is a stale endpoint to be deleted, so we expect a non-null post connect publisher. auto publisher = instance->createPostConnectOperation(); @@ -996,28 +999,28 @@ TEST_F( storedEndpoints->insert({staleEndpointAttributes.endpointId, staleEndpointConfig}); return true; })); - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([](CapabilitiesObserverInterface::State newState, - CapabilitiesObserverInterface::Error newError, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([](CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, std::vector addOrUpdateReportEndpointIdentifiers, std::vector deleteReportEndpointIdentifiers) { - EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED); - EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED); + EXPECT_EQ(newState, CapabilitiesDelegateObserverInterface::State::UNINITIALIZED); + EXPECT_EQ(newError, CapabilitiesDelegateObserverInterface::Error::UNINITIALIZED); EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector{}); EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector{}); })); auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); - instance->addCapabilitiesObserver(m_mockCapabilitiesObserver); + instance->addCapabilitiesObserver(m_mockCapabilitiesDelegateObserver); instance->addOrUpdateEndpoint(unchangedEndpointAttributes, unchangedCapabilityConfigs); /// Observer callback should only contain the pending endpoint to add (since that is already registered), /// but not the stale endpoint to delete (since that still needs to be sent to AVS). EXPECT_CALL( - *m_mockCapabilitiesObserver, + *m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, - CapabilitiesObserverInterface::Error::SUCCESS, + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, std::vector{unchangedEndpointAttributes.endpointId}, std::vector{})); @@ -1193,13 +1196,13 @@ TEST_F(CapabilitiesDelegateTest, test_reconnectWhenStorageIsEmpty) { .WillOnce(Return(true)); /// Expect calls to CapabilitiesObserver. - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([&](CapabilitiesObserverInterface::State state, - CapabilitiesObserverInterface::Error error, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([&](CapabilitiesDelegateObserverInterface::State state, + CapabilitiesDelegateObserverInterface::Error error, std::vector addedEndpoints, std::vector deletedEndpoints) { - EXPECT_EQ(state, CapabilitiesObserverInterface::State::SUCCESS); - EXPECT_EQ(error, CapabilitiesObserverInterface::Error::SUCCESS); + EXPECT_EQ(state, CapabilitiesDelegateObserverInterface::State::SUCCESS); + EXPECT_EQ(error, CapabilitiesDelegateObserverInterface::Error::SUCCESS); std::sort(addedEndpoints.begin(), addedEndpoints.end()); EXPECT_EQ(addedEndpoints, (std::vector{firstEndpointId, secondEndpointId})); @@ -1265,7 +1268,7 @@ TEST_F(CapabilitiesDelegateTest, test_deferSendDiscoveryEventsWhileDiscoveryEven /// Expect no callback to CapabilitiesObserver, since these endpoints remain in pending. EXPECT_CALL( - *m_mockCapabilitiesObserver, + *m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange( _, _, std::vector{firstEndpointId}, std::vector{secondEndpointId})) .Times(0); @@ -1317,21 +1320,24 @@ TEST_F(CapabilitiesDelegateTest, test_observerCallingIntoCapabilitiesDelegateOnS /// On the first successful capabilities change, call from observer back into CapabilitiesDelegate. EXPECT_CALL( - *m_mockCapabilitiesObserver, + *m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, CapabilitiesObserverInterface::Error::SUCCESS, _, _)) + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, + _, + _)) .Times(2) .WillOnce(Invoke([&, this]( - CapabilitiesObserverInterface::State state, - CapabilitiesObserverInterface::Error error, + CapabilitiesDelegateObserverInterface::State state, + CapabilitiesDelegateObserverInterface::Error error, std::vector addedEndpoints, std::vector deletedEndpoints) { /// If the below is false with "endpoint not registered", this means that the CapabilitiesDelegate /// state is not correct when it notifies its observers. ASSERT_TRUE(m_capabilitiesDelegate->deleteEndpoint(endpointAttributes, {capabilityConfig})); })) - .WillOnce(Invoke([&](CapabilitiesObserverInterface::State state, - CapabilitiesObserverInterface::Error error, + .WillOnce(Invoke([&](CapabilitiesDelegateObserverInterface::State state, + CapabilitiesDelegateObserverInterface::Error error, std::vector addedEndpoints, std::vector deletedEndpoints) { e.wakeUp(); })); @@ -1395,13 +1401,13 @@ TEST_F(CapabilitiesDelegateTest, test_reconnectTriggersSendPendingEndpoints) { .WillOnce(Return(true)); /// Expect calls to CapabilitiesObserver. - EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _)) - .WillOnce(Invoke([&](CapabilitiesObserverInterface::State state, - CapabilitiesObserverInterface::Error error, + EXPECT_CALL(*m_mockCapabilitiesDelegateObserver, onCapabilitiesStateChange(_, _, _, _)) + .WillOnce(Invoke([&](CapabilitiesDelegateObserverInterface::State state, + CapabilitiesDelegateObserverInterface::Error error, std::vector addedEndpoints, std::vector deletedEndpoints) { - EXPECT_EQ(state, CapabilitiesObserverInterface::State::SUCCESS); - EXPECT_EQ(error, CapabilitiesObserverInterface::Error::SUCCESS); + EXPECT_EQ(state, CapabilitiesDelegateObserverInterface::State::SUCCESS); + EXPECT_EQ(error, CapabilitiesDelegateObserverInterface::Error::SUCCESS); std::sort(addedEndpoints.begin(), addedEndpoints.end()); EXPECT_EQ(addedEndpoints, (std::vector{firstEndpointId, secondEndpointId})); diff --git a/CapabilitiesDelegate/test/MockCapabilitiesObserver.h b/CapabilitiesDelegate/test/MockCapabilitiesDelegateObserver.h similarity index 71% rename from CapabilitiesDelegate/test/MockCapabilitiesObserver.h rename to CapabilitiesDelegate/test/MockCapabilitiesDelegateObserver.h index 72fe9120e9..1a93fa434d 100644 --- a/CapabilitiesDelegate/test/MockCapabilitiesObserver.h +++ b/CapabilitiesDelegate/test/MockCapabilitiesDelegateObserver.h @@ -13,25 +13,24 @@ * permissions and limitations under the License. */ -#include +#include -#ifndef ALEXA_CLIENT_SDK_CAPABILITIESDELEGATE_TEST_MOCKCAPABILITIESOBSERVER_H_ -#define ALEXA_CLIENT_SDK_CAPABILITIESDELEGATE_TEST_MOCKCAPABILITIESOBSERVER_H_ +#ifndef ALEXA_CLIENT_SDK_CAPABILITIESDELEGATE_TEST_MOCKCAPABILITIESDELEGATEOBSERVER_H_ +#define ALEXA_CLIENT_SDK_CAPABILITIESDELEGATE_TEST_MOCKCAPABILITIESDELEGATEOBSERVER_H_ namespace alexaClientSDK { namespace capabilitiesDelegate { namespace test { - /** - * Mock class for @c CapabilitiesObserverInterface. + * Mock class for @c CapabilitiesDelegateObserverInterface. */ -class MockCapabilitiesObserver : public avsCommon::sdkInterfaces::CapabilitiesObserverInterface { +class MockCapabilitiesDelegateObserver : public avsCommon::sdkInterfaces::CapabilitiesDelegateObserverInterface { public: MOCK_METHOD4( onCapabilitiesStateChange, void( - CapabilitiesObserverInterface::State, - CapabilitiesObserverInterface::Error, + CapabilitiesDelegateObserverInterface::State, + CapabilitiesDelegateObserverInterface::Error, const std::vector&, const std::vector&)); }; @@ -40,4 +39,4 @@ class MockCapabilitiesObserver : public avsCommon::sdkInterfaces::CapabilitiesOb } // namespace capabilitiesDelegate } // namespace alexaClientSDK -#endif // ALEXA_CLIENT_SDK_CAPABILITIESDELEGATE_TEST_MOCKCAPABILITIESOBSERVER_H_ +#endif // ALEXA_CLIENT_SDK_CAPABILITIESDELEGATE_TEST_MOCKCAPABILITIESDELEGATEOBSERVER_H_ diff --git a/CapabilityAgents/AIP/CMakeLists.txt b/CapabilityAgents/AIP/CMakeLists.txt index 87ab693480..5423320869 100644 --- a/CapabilityAgents/AIP/CMakeLists.txt +++ b/CapabilityAgents/AIP/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(AIP LANGUAGES CXX) -include(${AVS_CORE}/build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/CapabilityAgents/AIP/include/AIP/AudioInputProcessor.h b/CapabilityAgents/AIP/include/AIP/AudioInputProcessor.h index 4f5fe55f5b..a15d7cbe0d 100644 --- a/CapabilityAgents/AIP/include/AIP/AudioInputProcessor.h +++ b/CapabilityAgents/AIP/include/AIP/AudioInputProcessor.h @@ -19,15 +19,19 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -39,11 +43,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -74,6 +80,7 @@ namespace aip { class AudioInputProcessor : public avsCommon::avs::CapabilityAgent , public avsCommon::sdkInterfaces::CapabilityConfigurationInterface + , public avsCommon::sdkInterfaces::LocaleAssetsObserverInterface , public avsCommon::sdkInterfaces::DialogUXStateObserverInterface , public avsCommon::sdkInterfaces::MessageRequestObserverInterface , public avsCommon::sdkInterfaces::InternetConnectionObserverInterface @@ -83,6 +90,21 @@ class AudioInputProcessor /// Alias to the @c AudioInputProcessorObserverInterface for brevity. using ObserverInterface = avsCommon::sdkInterfaces::AudioInputProcessorObserverInterface; + /** + * Request to configure @c AudioInputProcessor to provide multiple audio streams for a single Recognize event. Key + * in the map is resolve key, which will be used by caller to resolve the unresolved @c MessageRequest. Each resolve + * key correlates to a pair of encoding formats. The first format is the preferred format, and the second one is the + * fallback format which is used if the preferred one is not supported. + */ + using EncodingFormatRequest = std:: + map>; + + /** + * Response to caller when AIP receives EncodingFormatRequest. Key in the map is resolve key from the request, and + * corresponding value is the confirmed encoding format that AIP will provide for this resolve key. + */ + using EncodingFormatResponse = std::map; + /** * This function allows applications to tell the @c AudioInputProcessor that the @c ExpectSpeech directive's timeout * will be handled externally and stops the @c AudioInputProcessor from starting an internal timer to handle it. @@ -123,6 +145,8 @@ class AudioInputProcessor * @param assetsManager Responsible for retrieving and changing the wake words and locale. * @param wakeWordConfirmation The wake word confirmation setting. * @param speechConfirmation The end of speech confirmation setting. + * @param capabilityChangeNotifier The object with which to notify observers of @c AudioInputProcessor capability + * configurations change. * @param wakeWordsSetting The setting that represents the enabled wake words. This parameter is required if this * device supports wake words. * @param speechEncoder The Speech Encoder used to encode audio inputs. This parameter is optional and @@ -149,6 +173,7 @@ class AudioInputProcessor const std::shared_ptr& assetsManager, std::shared_ptr wakeWordConfirmation, std::shared_ptr speechConfirmation, + const std::shared_ptr& capabilityChangeNotifier, std::shared_ptr wakeWordsSetting = nullptr, std::shared_ptr speechEncoder = nullptr, AudioProvider defaultAudioProvider = AudioProvider::null(), @@ -255,6 +280,7 @@ class AudioInputProcessor /// @name MessageRequestObserverInterface Functions /// @{ + void onResponseStatusReceived(avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status status) override; void onSendCompleted(avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status status) override; void onExceptionReceived(const std::string& exceptionMessage) override; /// @} @@ -285,6 +311,11 @@ class AudioInputProcessor std::unordered_set> getCapabilityConfigurations() override; /// @} + /// @name LocaleAssetsObserverInterface Functions + /// @{ + void onLocaleAssetsChanged() override; + /// @} + /// @name InternetConnectionObserverInterface Functions /// @{ void onConnectionStatusChanged(bool connected) override; @@ -311,7 +342,42 @@ class AudioInputProcessor */ static settings::SettingEventMetadata getSpeechConfirmationMetadata(); + /** + * Set encoding for the audio format. The new encoding will be used for future utterances. Any audio stream already + * in progress will not be affected. This is an alternative API to @c requestEncodingAudioFormats(), but will + * configure @c AudioInputProcessor to only produce one audio stream. + * + * @param encoding The encoding format to use. + * @return @true on success, @c false on failure to set the encoding. + */ + bool setEncodingAudioFormat(avsCommon::utils::AudioFormat::Encoding encoding); + + /** + * Function to request multiple audio streams from @c AudioInputProcessor in a single Recognize event. This is an + * alternative API to @c setEncodingAudioFormat(). + * @param encodings A map of resolveKey to a pair of encoding formats. Each resolveKey stands for an audio stream. + * The first encoding format is the requested format, and the second one is backup format if the requested one isn't + * supported by @c AudioInputProcessor. + * @return A map of resolveKey to result encoding format. If the requested format is supported, the result will be + * the requested one. If not, @c AudioInputProcessor will check backup format. If neither of them is supported, the + * corresponding resolve key will be removed from result. + */ + EncodingFormatResponse requestEncodingAudioFormats(const EncodingFormatRequest& encodings); + + /** + * Function to get encoding audio formats. + * @return Encoding formats requested for multiple audio streams. + */ + EncodingFormatResponse getEncodingAudioFormats() const; + private: + /** + * Inintialize audio input processor. + * + * @return @c true on success, @c false on failure to initialize + */ + bool initialize(); + /** * Constructor. * @@ -329,6 +395,8 @@ class AudioInputProcessor * to @c AudioProvider::null(). * @param wakeWordConfirmation The wake word confirmation setting. * @param speechConfirmation The end of speech confirmation setting. + * @param capabilityChangeNotifier The object with which to notify observers of @c AudioInputProcessor capability + * configurations change. * @param wakeWordsSetting The setting that represents the enabled wake words. This parameter is required if this * device supports wake words. * @param capabilitiesConfiguration The SpeechRecognizer capabilities configuration. @@ -350,10 +418,12 @@ class AudioInputProcessor std::shared_ptr exceptionEncounteredSender, std::shared_ptr userInactivityMonitor, std::shared_ptr systemSoundPlayer, + const std::shared_ptr& assetsManager, std::shared_ptr speechEncoder, AudioProvider defaultAudioProvider, std::shared_ptr wakeWordConfirmation, std::shared_ptr speechConfirmation, + const std::shared_ptr& capabilityChangeNotifier, std::shared_ptr wakeWordsSetting, std::shared_ptr capabilitiesConfiguration, std::shared_ptr powerResourceManager, @@ -571,6 +641,11 @@ class AudioInputProcessor */ void executeDisconnected(); + /** + * This function is called whenever locale assets of @c LocaleAssetsManagerInterface are changed. + */ + void executeOnLocaleAssetsChanged(); + /** * This function updates the @c AudioInputProcessor state and notifies the state observer. Any changes to * @c m_state should be made through this function. @@ -621,6 +696,44 @@ class AudioInputProcessor */ void managePowerResource(ObserverInterface::State newState); + /** + * Helper function to create @c MessageRequestResolveFunction for ongoing Recognize event. + * @return MessageRequestResolver function with respect to current @c m_encodingAudioFormats, and attachment readers + * @note This function is not thread-safe, caller should requires @c m_encodingFormatMutex for synchronization + */ + avsCommon::avs::MessageRequest::MessageRequestResolveFunction getMessageRequestResolverLocked() const; + + /** + * Helper function to close both KWD metadata and audio attachment readers in @c AudioInputProcessor regardless + * how many streams it's configured to produce. + * @param closePoint Close the reader immediately or after draining buffer + * @note For the sake of thread safety, this helper function can only be called by @c m_executor thread + */ + void closeAttachmentReaders( + avsCommon::avs::attachment::AttachmentReader::ClosePoint closePoint = + avsCommon::avs::attachment::AttachmentReader::ClosePoint::AFTER_DRAINING_CURRENT_BUFFER); + + /** + * Helper function to check whether provided encoding audio format is supported by audio stream provider or encoder. + * @param encodingFormat Encoding format to check. + * @return @c true if it's supported, else @c false + */ + bool isEncodingFormatSupported(avsCommon::utils::AudioFormat::Encoding encodingFormat) const; + + /** + * Helper function to indicate if audio encoder is being used. + * @return @c true if encoder is used, else @c false + * @note This function is not thread-safe, caller should requires @c m_encodingFormatMutex for synchronization + */ + bool isUsingEncoderLocked() const; + + /** + * Helper function to indicate if @c AudioInputProcessor is configured to produce multiple audio streams + * @return @c true if multiple streams are being requested, else @c false + * @note This function is not thread-safe, caller should acquire @c m_encodingFormatMutex for synchronization + */ + bool multiStreamsRequestedLocked() const; + /// The metric recorder. std::shared_ptr m_metricRecorder; @@ -643,7 +756,7 @@ class AudioInputProcessor avsCommon::utils::timing::Timer m_expectingSpeechTimer; /// The Speech Encoder to encode input stream. - std::shared_ptr m_encoder; + const std::shared_ptr m_encoder; /** * @name Executor Thread Variables @@ -669,18 +782,15 @@ class AudioInputProcessor AudioProvider m_lastAudioProvider; /** - * The attachment reader which is currently being used to stream audio for a Recognize event. This pointer is - * valid during the @c RECOGNIZING state, and is retained by @c AudioInputProcessor so that it can close the - * stream from @c executeStopCapture(). + * The attachment readers which are currently being used to stream audio for a Recognize event. These readers are + * valid during the @c RECOGNIZING state, and are retained by @c AudioInputProcessor so that they can close the + * stream from @c executeStopCapture(). These attachment readers are grouped by resolve key, which aims to support + * multiple audio stream use case. Attachment readers include both KWD metadata and audio stream, and they're + * ordered properly in the vector, and ready to use to assemble @c MessageRequest. + * @note For the sake of thread safety, this member data should only be updated by @c m_executor thread. */ - std::shared_ptr m_reader; - - /** - * The attachment reader used for the wake word engine metadata. It's is populated by a call to @c - * executeRecognize(), and later consumed by a call to @c executeOnContextAvailable() when the context arrives and - * the full @c MessageRequest can be assembled. This reader is only relevant during the @c RECOGNIZING state. - */ - std::shared_ptr m_KWDMetadataReader; + std::unordered_map>> + m_attachmentReaders; /** * The payload for a Recognize event. This string is populated by a call to @c executeRecognize(), and later @@ -727,9 +837,17 @@ class AudioInputProcessor */ bool m_localStopCapturePerformed; + /** + * This flag indicates if the event stream is closed while the device is still in @c RECOGNIZING. + */ + bool m_streamIsClosedInRecognizingState; + /// The system sound player. std::shared_ptr m_systemSoundPlayer; + /// The locale assets manager. + std::shared_ptr m_assetsManager; + /** * The initiator value from the preceding ExpectSpeech directive. The ExpectSpeech directive's initiator * will need to be consumed and sent back in a subsequent Recognize event. This should be cleared if the @@ -751,6 +869,9 @@ class AudioInputProcessor /// The end of speech confirmation setting. std::shared_ptr m_speechConfirmation; + /// The object to notify of @c AudioInputProcessor capability configurations change. + std::shared_ptr m_capabilityChangeNotifier; + /// The wake words setting. This field is optional and it is only used if the device supports wake words. std::shared_ptr m_wakeWordsSetting; @@ -766,14 +887,6 @@ class AudioInputProcessor */ std::shared_ptr m_expectSpeechTimeoutHandler; - /** - * @c Executor which queues up operations from asynchronous API calls. - * - * @note This declaration needs to come *after* the Executor Thread Variables above so that the thread shuts down - * before the Executor Thread Variables are destroyed. - */ - avsCommon::utils::threading::Executor m_executor; - /** * Temporary value of dialogRequestId generated when onRecognize starts. This should be cleared after being * passed to the directive sequencer. @@ -784,6 +897,43 @@ class AudioInputProcessor * Value that will contain the time since last wake from suspend when AIP acquires the wakelock. */ std::chrono::milliseconds m_timeSinceLastResumeMS; + + /** + * Value to indicate if audio encoder is being used. + */ + bool m_usingEncoder; + + /** + * Mutex to serialize access to m_capabilityConfigurations. + */ + std::mutex m_mutex; + + /** + * Resolve function to pass to @c MessageRequest when multiple audio streams are requested. + */ + avsCommon::avs::MessageRequest::MessageRequestResolveFunction m_messageRequestResolver; + + /** + * Encoding formats used to encode audio streams. This is a map from resolveKey to corresponding encoding format. If + * there is only one format in the map, @c AudioInputProcess will send resolved @c MessageRequest, and the + * resolveKey will be ignored. If there are multiple formats in the map, @c AudioInputProcessor will send unresolved + * @c MessageRequest with audio streams of all provided formats, and user needs to use the corresponding resolveKey + * to resolve the @c MessageRequest to a valid one containing only the audio with the right format. + */ + EncodingFormatResponse m_encodingAudioFormats; + + /** + * Mutex to synchronize updates to @c m_encodingAudioFormats. + */ + mutable std::mutex m_encodingFormatMutex; + + /** + * @c Executor which queues up operations from asynchronous API calls. + * + * @note This declaration needs to come *after* the Executor Thread Variables above so that the thread shuts down + * before the Executor Thread Variables are destroyed. + */ + avsCommon::utils::threading::Executor m_executor; }; } // namespace aip diff --git a/CapabilityAgents/AIP/src/AudioInputProcessor.cpp b/CapabilityAgents/AIP/src/AudioInputProcessor.cpp index 4f9acc8916..3acd19b08b 100644 --- a/CapabilityAgents/AIP/src/AudioInputProcessor.cpp +++ b/CapabilityAgents/AIP/src/AudioInputProcessor.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -49,6 +48,7 @@ using namespace avsCommon::avs; using namespace avsCommon::utils; using namespace avsCommon::utils::logger; using namespace avsCommon::utils::metrics; +using namespace avsCommon::utils::json; using namespace avsCommon::sdkInterfaces; using namespace std::chrono; @@ -210,11 +210,29 @@ static const std::string STOP_CAPTURE_TO_END_OF_SPEECH_METRIC_NAME = "STOP_CAPTU static const std::string STOP_CAPTURE_TO_END_OF_SPEECH_ACTIVITY_NAME = METRIC_ACTIVITY_NAME_PREFIX_AIP + STOP_CAPTURE_TO_END_OF_SPEECH_METRIC_NAME; +/// The default resolveKey used as a placeholder when only one encoding format is configured for @c AudioInputProcessor +static const std::string DEFAULT_RESOLVE_KEY = "DEFAULT_RESOLVE_KEY"; + /// Preroll duration is a fixed 500ms. static const std::chrono::milliseconds PREROLL_DURATION = std::chrono::milliseconds(500); static const int MILLISECONDS_PER_SECOND = 1000; +/** + * Helper function to get string values of encoding audio format, which are used in Recognize event. + * @param encoding Target encoding format + * @return String name of the encoding format + */ +static std::string encodingFormatToString(avsCommon::utils::AudioFormat::Encoding encoding) { + switch (encoding) { + case avsCommon::utils::AudioFormat::Encoding::OPUS: + return "OPUS"; + case avsCommon::utils::AudioFormat::Encoding::LPCM: + return "AUDIO_L16_RATE_16000_CHANNELS_1"; + } + return "UNKNOWN"; +} + /** * Handles a Metric event by creating and recording it. Failure to create or record the event results * in an early return. @@ -282,6 +300,22 @@ static void submitMetric( static std::shared_ptr getSpeechRecognizerCapabilityConfiguration( const LocaleAssetsManagerInterface& assetsManager); +/** + * Resolving method to be called in lambda function that will be passed to @c MessageRequest as @c + * MessageRequestResolveFunction + * @param[in,out] request @c EditableMessageRequest to resolve + * @param resolveKey Provided resolve key + * @param encodingFormats Mappings from resolveKey to encoding format + * @param attachmentReaders Mappings from resolveKey to attachment readers + * @return @c true if the message is resolved successfully, else @c false + */ +static bool resolveMessageRequest( + const std::shared_ptr& request, + const std::string& resolveKey, + AudioInputProcessor::EncodingFormatResponse encodingFormats, + const std::unordered_map>>& + attachmentReaders); + std::shared_ptr AudioInputProcessor::create( std::shared_ptr directiveSequencer, std::shared_ptr messageSender, @@ -294,6 +328,7 @@ std::shared_ptr AudioInputProcessor::create( const std::shared_ptr& assetsManager, std::shared_ptr wakeWordConfirmation, std::shared_ptr speechConfirmation, + const std::shared_ptr& capabilityChangeNotifier, std::shared_ptr wakeWordsSetting, std::shared_ptr speechEncoder, AudioProvider defaultAudioProvider, @@ -336,6 +371,9 @@ std::shared_ptr AudioInputProcessor::create( } else if (!assetsManager->getDefaultSupportedWakeWords().empty() && !wakeWordsSetting) { ACSDK_ERROR(LX("createFailed").d("reason", "nullWakeWordsSetting")); return nullptr; + } else if (!capabilityChangeNotifier) { + ACSDK_ERROR(LX("createFailed").d("reason", "nullCapabilityChangeNotifier")); + return nullptr; } auto capabilitiesConfiguration = getSpeechRecognizerCapabilityConfiguration(*assetsManager); @@ -343,7 +381,6 @@ std::shared_ptr AudioInputProcessor::create( ACSDK_ERROR(LX("createFailed").d("reason", "unableToCreateCapabilitiesConfiguration")); return nullptr; } - auto aip = std::shared_ptr(new AudioInputProcessor( directiveSequencer, messageSender, @@ -352,20 +389,26 @@ std::shared_ptr AudioInputProcessor::create( exceptionEncounteredSender, userInactivityMonitor, systemSoundPlayer, + assetsManager, speechEncoder, defaultAudioProvider, wakeWordConfirmation, speechConfirmation, + capabilityChangeNotifier, wakeWordsSetting, capabilitiesConfiguration, powerResourceManager, std::move(metricRecorder), expectSpeechTimeoutHandler)); + if (!aip->initialize()) { + ACSDK_ERROR(LX("createFailed").d("reason", "unableToInitialize")); + return nullptr; + } + if (aip) { dialogUXStateAggregator->addObserver(aip); } - return aip; } @@ -451,6 +494,7 @@ std::future AudioInputProcessor::stopCapture() { } std::future AudioInputProcessor::resetState() { + ACSDK_DEBUG0(LX(__func__)); return m_executor.submit([this]() { executeResetState(); }); } @@ -541,10 +585,12 @@ AudioInputProcessor::AudioInputProcessor( std::shared_ptr exceptionEncounteredSender, std::shared_ptr userInactivityMonitor, std::shared_ptr systemSoundPlayer, + const std::shared_ptr& assetsManager, std::shared_ptr speechEncoder, AudioProvider defaultAudioProvider, std::shared_ptr wakeWordConfirmation, std::shared_ptr speechConfirmation, + const std::shared_ptr& capabilityChangeNotifier, std::shared_ptr wakeWordsSetting, std::shared_ptr capabilitiesConfiguration, std::shared_ptr powerResourceManager, @@ -561,20 +607,25 @@ AudioInputProcessor::AudioInputProcessor( m_encoder{speechEncoder}, m_defaultAudioProvider{defaultAudioProvider}, m_lastAudioProvider{AudioProvider::null()}, - m_KWDMetadataReader{nullptr}, m_state{ObserverInterface::State::IDLE}, m_focusState{avsCommon::avs::FocusState::NONE}, m_preparingToSend{false}, m_initialDialogUXStateReceived{false}, m_localStopCapturePerformed{false}, + m_streamIsClosedInRecognizingState{false}, m_systemSoundPlayer{systemSoundPlayer}, + m_assetsManager{assetsManager}, m_precedingExpectSpeechInitiator{nullptr}, m_wakeWordConfirmation{wakeWordConfirmation}, m_speechConfirmation{speechConfirmation}, + m_capabilityChangeNotifier{capabilityChangeNotifier}, m_wakeWordsSetting{wakeWordsSetting}, m_powerResourceManager{powerResourceManager}, m_expectSpeechTimeoutHandler{expectSpeechTimeoutHandler}, - m_timeSinceLastResumeMS{std::chrono::milliseconds(0)} { + m_timeSinceLastResumeMS{std::chrono::milliseconds(0)}, + m_usingEncoder{false}, + m_messageRequestResolver{nullptr}, + m_encodingAudioFormats{{DEFAULT_RESOLVE_KEY, AudioFormat::Encoding::LPCM}} { m_capabilityConfigurations.insert(capabilitiesConfiguration); } @@ -617,7 +668,7 @@ std::shared_ptr getSpeechRecognizerCapa json::JsonGenerator generator; generator.addMembersArray(CAPABILITY_INTERFACE_WAKE_WORDS_KEY, wakeWords); configMap.insert({CAPABILITY_INTERFACE_CONFIGURATIONS_KEY, generator.toString()}); - ACSDK_DEBUG7(LX(__func__).d("wakeWords", generator.toString())); + ACSDK_DEBUG5(LX(__func__).d("wakeWords", generator.toString())); } return std::make_shared(configMap); @@ -640,6 +691,60 @@ void AudioInputProcessor::doShutdown() { m_expectSpeechTimeoutHandler.reset(); } +bool resolveMessageRequest( + const std::shared_ptr& request, + const std::string& resolveKey, + AudioInputProcessor::EncodingFormatResponse encodingFormats, + const std::unordered_map>>& + attachmentReaders) { + if (!request) { + ACSDK_ERROR(LX("Failed to resolve MessageRequest") + .d("reason", "Null pointer to MessageRequest") + .d("value", resolveKey)); + return false; + } + + if (encodingFormats.find(resolveKey) == encodingFormats.end()) { + ACSDK_ERROR(LX("Failed to resolve MessageRequest").d("reason", "Invalid resolveKey").d("value", resolveKey)); + return false; + } + + rapidjson::Document eventWithContext; + jsonUtils::parseJSON(request->getJsonContent(), &eventWithContext); + auto event = eventWithContext.FindMember("event"); + if (event == eventWithContext.MemberEnd()) { + ACSDK_ERROR(LX("Failed to resolve MessageRequest").d("reason", "No event found in json")); + return false; + } + auto payload = event->value.FindMember("payload"); + if (payload == event->value.MemberEnd()) { + ACSDK_ERROR(LX("Failed to resolve MessageRequest").d("reason", "No payload in json")); + return false; + } + + rapidjson::Value formatValue; + auto formatString = encodingFormatToString(encodingFormats.at(resolveKey)); + formatValue.SetString(formatString.c_str(), formatString.length()); + + if (payload->value.FindMember(FORMAT_KEY) != payload->value.MemberEnd()) { + ACSDK_WARN(LX("Format already exists in Json payload. Replace it with").d("format", formatString)); + payload->value.FindMember(FORMAT_KEY)->value.SetString(formatString.c_str(), eventWithContext.GetAllocator()); + } else { + payload->value.AddMember( + rapidjson::StringRef(FORMAT_KEY.c_str()), formatValue, eventWithContext.GetAllocator()); + } + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + eventWithContext.Accept(writer); + + std::string jsonContent(buffer.GetString()); + request->setJsonContent(jsonContent); + if (attachmentReaders.find(resolveKey) != attachmentReaders.end()) { + request->setAttachmentReaders(attachmentReaders.at(resolveKey)); + } + return true; +} + std::future AudioInputProcessor::expectSpeechTimedOut() { return m_executor.submit([this]() { return executeExpectSpeechTimedOut(); }); } @@ -818,7 +923,7 @@ bool AudioInputProcessor::executeRecognize( return false; } - avsEncodingFormat = "AUDIO_L16_RATE_16000_CHANNELS_1"; + avsEncodingFormat = encodingFormatToString(provider.format.encoding); break; case avsCommon::utils::AudioFormat::Encoding::OPUS: itSampleRateAVSEncoding = mapSampleRatesAVSEncoding.find(provider.format.sampleRateHz); @@ -829,7 +934,7 @@ bool AudioInputProcessor::executeRecognize( return false; } - avsEncodingFormat = itSampleRateAVSEncoding->second; + avsEncodingFormat = encodingFormatToString(provider.format.encoding); break; default: ACSDK_ERROR(LX("executeRecognizeFailed") @@ -866,11 +971,10 @@ bool AudioInputProcessor::executeRecognize( return false; } // For barge-in, we should close the previous reader before creating another one. - if (m_reader) { - m_reader->close( - avsCommon::avs::attachment::AttachmentReader::ClosePoint::AFTER_DRAINING_CURRENT_BUFFER); - m_reader.reset(); + if (m_usingEncoder) { + m_encoder->stopEncoding(true); } + closeAttachmentReaders(); break; case ObserverInterface::State::BUSY: ACSDK_ERROR(LX("executeRecognizeFailed").d("reason", "Barge-in is not permitted while busy")); @@ -882,17 +986,20 @@ bool AudioInputProcessor::executeRecognize( [this]() { m_systemSoundPlayer->playTone(SystemSoundPlayerInterface::Tone::WAKEWORD_NOTIFICATION); }); } - // Check if SpeechEncoder is available - const bool shouldBeEncoded = (m_encoder != nullptr); - // Set up format - if (shouldBeEncoded && m_encoder->getContext()) { - avsEncodingFormat = m_encoder->getContext()->getAVSFormatName(); - } + // Mutex required since following operations depend on value of @c m_encodingAudioFormats + std::lock_guard lock(m_encodingFormatMutex); // Assemble the event payload. json::JsonGenerator payloadGenerator; payloadGenerator.addMember(PROFILE_KEY, asrProfileToString(provider.profile)); - payloadGenerator.addMember(FORMAT_KEY, avsEncodingFormat); + m_usingEncoder = isUsingEncoderLocked(); + if (!multiStreamsRequestedLocked()) { + // Set up format for single audio stream request + if (m_usingEncoder && m_encoder->getContext()) { + avsEncodingFormat = m_encoder->getContext()->getAVSFormatName(); + } + payloadGenerator.addMember(FORMAT_KEY, avsEncodingFormat); + } // The initiator (or lack thereof) from a previous ExpectSpeech has precedence. if (m_precedingExpectSpeechInitiator) { @@ -915,36 +1022,61 @@ bool AudioInputProcessor::executeRecognize( reference = AudioInputStream::Reader::Reference::ABSOLUTE; } + avsCommon::avs::AudioInputStream::Index encodingOffset = 0; + AudioInputStream::Reader::Reference encodingReference = AudioInputStream::Reader::Reference::ABSOLUTE; + // Set up the speech encoder - if (shouldBeEncoded) { + if (m_usingEncoder) { ACSDK_DEBUG(LX("encodingAudio").d("format", avsEncodingFormat)); if (!m_encoder->startEncoding(provider.stream, provider.format, offset, reference)) { ACSDK_ERROR(LX("executeRecognizeFailed").d("reason", "Failed to start encoder")); return false; } - offset = 0; - reference = AudioInputStream::Reader::Reference::BEFORE_WRITER; } else { ACSDK_DEBUG(LX("notEncodingAudio")); } - m_reader = attachment::DefaultAttachmentReader::create( - sds::ReaderPolicy::NONBLOCKING, - shouldBeEncoded ? m_encoder->getEncodedStream() : provider.stream, - offset, - reference); - if (!m_reader) { - ACSDK_ERROR(LX("executeRecognizeFailed").d("reason", "Failed to create attachment reader")); - return false; + if (KWDMetadata) { + for (auto& it : m_encodingAudioFormats) { + std::shared_ptr metadataReader = + attachment::AttachmentUtils::createAttachmentReader(*KWDMetadata); + if (!metadataReader) { + ACSDK_ERROR(LX("sendingKWDMetadataFailed").d("reason", "Failed to create attachment reader")); + // If fail to create any metadata reader, clear all existing metadata for consistency + closeAttachmentReaders(attachment::AttachmentReader::ClosePoint::IMMEDIATELY); + break; + } + auto resolveKey = it.first; + m_attachmentReaders[resolveKey] = { + std::make_shared(KWD_METADATA_FIELD_NAME, metadataReader)}; + } } - if (KWDMetadata) { - m_KWDMetadataReader = avsCommon::avs::attachment::AttachmentUtils::createAttachmentReader(*KWDMetadata); - if (!m_KWDMetadataReader) { - ACSDK_ERROR(LX("sendingKWDMetadataFailed").d("reason", "Failed to create attachment reader")); + // N audio streams of different encodings + for (auto& it : m_encodingAudioFormats) { + ACSDK_DEBUG( + LX("Create audio reader").d("format", it.second).d("offset", offset).d("encodingOffset", encodingOffset)); + std::shared_ptr audioReader = + attachment::DefaultAttachmentReader::create( + sds::ReaderPolicy::NONBLOCKING, + it.second == AudioFormat::Encoding::LPCM ? provider.stream : m_encoder->getEncodedStream(), + it.second == AudioFormat::Encoding::LPCM ? offset : encodingOffset, + it.second == AudioFormat::Encoding::LPCM ? reference : encodingReference); + if (!audioReader) { + ACSDK_ERROR(LX("executeRecognizeFailed").d("reason", "Failed to create attachment reader")); + closeAttachmentReaders(); + return false; } + auto resolveKey = it.first; + m_attachmentReaders[resolveKey].push_back( + std::make_shared(AUDIO_ATTACHMENT_FIELD_NAME, audioReader)); + ACSDK_INFO(LX("Create audio attachment reader success") + .d("resolveKey", resolveKey) + .d("format", encodingFormatToString(it.second))); } + m_messageRequestResolver = multiStreamsRequestedLocked() ? getMessageRequestResolverLocked() : nullptr; + // Code below this point changes the state of AIP. Formally update state now, and don't error out without calling // executeResetState() after this point. m_preCachedDialogRequestId = uuidGeneration::generateUUID(); @@ -956,6 +1088,7 @@ bool AudioInputProcessor::executeRecognize( // Reset flag when we send a new recognize event. m_localStopCapturePerformed = false; + m_streamIsClosedInRecognizingState = false; // Start assembling the context; we'll service the callback after assembling our Recognize event. m_contextManager->getContextWithoutReportableStateProperties(shared_from_this()); @@ -1019,7 +1152,7 @@ void AudioInputProcessor::executeOnContextAvailable(const std::string jsonContex } // Should already have a reader. - if (!m_reader) { + if (m_attachmentReaders.empty()) { ACSDK_ERROR(LX("executeOnContextAvailableFailed").d("reason", "nullReader")); executeResetState(); return; @@ -1050,15 +1183,25 @@ void AudioInputProcessor::executeOnContextAvailable(const std::string jsonContex // Assemble the MessageRequest. It will be sent by executeOnFocusChanged when we acquire the channel. auto msgIdAndJsonEvent = buildJsonEventString("Recognize", m_directiveSequencer->getDialogRequestId(), m_recognizePayload, jsonContext); - m_recognizeRequest = std::make_shared(msgIdAndJsonEvent.second); - if (m_KWDMetadataReader) { - m_recognizeRequest->addAttachmentReader(KWD_METADATA_FIELD_NAME, m_KWDMetadataReader); + if (m_messageRequestResolver) { + // Create unresolved MessageRequest + m_recognizeRequest = std::make_shared( + msgIdAndJsonEvent.second, + true, + "", + std::vector>{}, + m_messageRequestResolver); + } else { + m_recognizeRequest = std::make_shared(msgIdAndJsonEvent.second); + // Only add attachment readers for resolved MessageRequest. For unresolved one, attachment readers will be + // passed to the resolver function. + if (!m_attachmentReaders.empty() && !m_attachmentReaders.begin()->second.empty()) { + for (auto& namedReader : m_attachmentReaders.begin()->second) { + m_recognizeRequest->addAttachmentReader(namedReader->name, namedReader->reader); + } + } } - m_recognizeRequest->addAttachmentReader(AUDIO_ATTACHMENT_FIELD_NAME, m_reader); - - // Release ownership of the metadata so it can be released once ACL will finish sending the message. - m_KWDMetadataReader.reset(); // If we already have focus, there won't be a callback to send the message, so send it now. if (avsCommon::avs::FocusState::FOREGROUND == m_focusState) { @@ -1104,14 +1247,18 @@ bool AudioInputProcessor::executeStopCapture(bool stopImmediately, std::shared_p auto returnValue = false; if (info) { if (info->result) { - if (m_localStopCapturePerformed) { - // Since a local StopCapture was performed, we can safely ignore the StopCapture from AVS. + if (m_localStopCapturePerformed || m_streamIsClosedInRecognizingState) { + // Since a local StopCapture was performed or the event stream is closed while in RECOGNIZING state, + // we can safely ignore the StopCapture from AVS. + ACSDK_INFO(LX("executeStopCapture") + .d("localStopCapturePerformed", m_localStopCapturePerformed) + .d("streamIsClosedInRecognizingState", m_streamIsClosedInRecognizingState) + .m("StopCapture directive ignored because local StopCapture was performed or event" + " stream is closed while in recognizing state.")); m_localStopCapturePerformed = false; + m_streamIsClosedInRecognizingState = false; returnValue = true; info->result->setCompleted(); - ACSDK_INFO(LX("executeStopCapture") - .m("StopCapture directive ignored because local StopCapture was performed.")); - } else { static const char* errorMessage = "StopCapture only allowed in RECOGNIZING state."; info->result->setFailed(errorMessage); @@ -1134,20 +1281,15 @@ bool AudioInputProcessor::executeStopCapture(bool stopImmediately, std::shared_p // Create a lambda to do the StopCapture. std::function stopCapture = [=] { ACSDK_DEBUG(LX("stopCapture").d("stopImmediately", stopImmediately)); - if (m_encoder) { + if (m_usingEncoder) { // If SpeechEncoder is enabled, let it finish it so the stream will be closed automatically. m_encoder->stopEncoding(stopImmediately); - } else { - // Otherwise close current reader manually. - if (stopImmediately) { - m_reader->close(avsCommon::avs::attachment::AttachmentReader::ClosePoint::IMMEDIATELY); - } else { - m_reader->close( - avsCommon::avs::attachment::AttachmentReader::ClosePoint::AFTER_DRAINING_CURRENT_BUFFER); - } + m_usingEncoder = false; } + auto closePoint = stopImmediately ? attachment::AttachmentReader::ClosePoint::IMMEDIATELY + : attachment::AttachmentReader::ClosePoint::AFTER_DRAINING_CURRENT_BUFFER; + closeAttachmentReaders(closePoint); - m_reader.reset(); setState(ObserverInterface::State::BUSY); if (info) { @@ -1178,14 +1320,10 @@ void AudioInputProcessor::executeResetState() { ACSDK_DEBUG(LX(__func__)); m_expectingSpeechTimer.stop(); m_precedingExpectSpeechInitiator.reset(); - if (m_reader) { - m_reader->close(); - } + closeAttachmentReaders(); if (m_encoder) { m_encoder->stopEncoding(true); } - m_reader.reset(); - m_KWDMetadataReader.reset(); if (m_recognizeRequestSent) { m_recognizeRequestSent->removeObserver(shared_from_this()); m_recognizeRequestSent.reset(); @@ -1272,7 +1410,7 @@ bool AudioInputProcessor::executeExpectSpeechTimedOut() { } m_precedingExpectSpeechInitiator.reset(); auto msgIdAndJsonEvent = buildJsonEventString("ExpectSpeechTimedOut"); - auto request = std::make_shared(msgIdAndJsonEvent.second); + auto request = std::make_shared(msgIdAndJsonEvent.second); request->addObserver(shared_from_this()); m_messageSender->sendMessage(request); setState(ObserverInterface::State::IDLE); @@ -1281,6 +1419,8 @@ bool AudioInputProcessor::executeExpectSpeechTimedOut() { } void AudioInputProcessor::executeOnDialogUXStateChanged(DialogUXStateObserverInterface::DialogUXState newState) { + ACSDK_DEBUG0(LX(__func__).d("newState", newState)); + if (!m_initialDialogUXStateReceived) { // The initial dialog UX state change call comes from simply registering as an observer; it is not a deliberate // change to the dialog state which should interrupt a recognize event. @@ -1342,6 +1482,14 @@ void AudioInputProcessor::sendRequestNow() { } } +void AudioInputProcessor::onResponseStatusReceived( + avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status status) { + ACSDK_DEBUG(LX("onResponseStatusReceived").d("status", status)); + if (status != MessageRequestObserverInterface::Status::SUCCESS) { + resetState(); + } +} + void AudioInputProcessor::onExceptionReceived(const std::string& exceptionMessage) { ACSDK_ERROR(LX("onExceptionReceived").d("exception", exceptionMessage)); resetState(); @@ -1349,24 +1497,40 @@ void AudioInputProcessor::onExceptionReceived(const std::string& exceptionMessag void AudioInputProcessor::onSendCompleted(MessageRequestObserverInterface::Status status) { ACSDK_DEBUG(LX("onSendCompleted").d("status", status)); - - if (status == MessageRequestObserverInterface::Status::SUCCESS || - status == MessageRequestObserverInterface::Status::PENDING) { - // Stop listening from audio input source when the recognize event steam is closed. - ACSDK_DEBUG5(LX("stopCapture").d("reason", "streamClosed")); - stopCapture(); - - return; - } - ACSDK_DEBUG(LX("resetState").d("dueToStatus", status)); - resetState(); + m_executor.submit([this, status]() { + if (MessageRequestObserverInterface::Status::SUCCESS == status && + ObserverInterface::State::RECOGNIZING == m_state) { + // This is to take care of the edge case where the event stream is closed before a stop capture is received. + m_streamIsClosedInRecognizingState = true; + } + executeResetState(); + }); } std::unordered_set> AudioInputProcessor:: getCapabilityConfigurations() { + std::lock_guard lock(m_mutex); return m_capabilityConfigurations; } +void AudioInputProcessor::onLocaleAssetsChanged() { + m_executor.submit([this]() { executeOnLocaleAssetsChanged(); }); +} + +void AudioInputProcessor::executeOnLocaleAssetsChanged() { + ACSDK_DEBUG(LX(__func__)); + std::unique_lock lock(m_mutex); + m_capabilityConfigurations.clear(); + auto newWakeWordsConfiguration = getSpeechRecognizerCapabilityConfiguration(*m_assetsManager); + m_capabilityConfigurations.insert(newWakeWordsConfiguration); + lock.unlock(); + m_capabilityChangeNotifier->notifyObservers( + [newWakeWordsConfiguration]( + std::shared_ptr observer) { + observer->onConfigurationChanged(*newWakeWordsConfiguration); + }); +} + void AudioInputProcessor::handleDirectiveFailure( const std::string& errorMessage, std::shared_ptr info, @@ -1531,6 +1695,104 @@ void AudioInputProcessor::executeDisconnected() { } } +bool AudioInputProcessor::setEncodingAudioFormat(AudioFormat::Encoding encoding) { + if (encoding == AudioFormat::Encoding::LPCM || + (m_encoder && m_encoder->getContext() && encoding == m_encoder->getContext()->getAudioFormat().encoding)) { + std::lock_guard lock(m_encodingFormatMutex); + m_encodingAudioFormats.clear(); + // Only one format is configured, and AIP will send resolved RequestMessage, and this resolveKey is simply a + // placeholder + m_encodingAudioFormats.emplace(DEFAULT_RESOLVE_KEY, encoding); + return true; + } + return false; +} + +bool AudioInputProcessor::initialize() { + if (m_encoder && m_encoder->getContext()) { + std::lock_guard lock(m_encodingFormatMutex); + m_encodingAudioFormats.clear(); + m_encodingAudioFormats.emplace(DEFAULT_RESOLVE_KEY, m_encoder->getContext()->getAudioFormat().encoding); + } + m_assetsManager->addLocaleAssetsObserver(shared_from_this()); + return true; +} + +MessageRequest::MessageRequestResolveFunction AudioInputProcessor::getMessageRequestResolverLocked() const { + auto encodingAudioFormats = m_encodingAudioFormats; + auto attachmentReaders = m_attachmentReaders; + MessageRequest::MessageRequestResolveFunction resolver = [encodingAudioFormats, attachmentReaders]( + const std::shared_ptr& req, + std::string resolveKey) { + return resolveMessageRequest(req, resolveKey, encodingAudioFormats, attachmentReaders); + }; + return resolver; +} + +AudioInputProcessor::EncodingFormatResponse AudioInputProcessor::requestEncodingAudioFormats( + const EncodingFormatRequest& encodings) { + EncodingFormatResponse result; + for (auto it = encodings.begin(); it != encodings.end(); it++) { + auto requestedEncoding = it->second.first; + auto fallbackEncoding = it->second.second; + if (isEncodingFormatSupported(requestedEncoding)) { + result[it->first] = requestedEncoding; + } else if (isEncodingFormatSupported(fallbackEncoding)) { + result[it->first] = fallbackEncoding; + } else { + ACSDK_WARN(LX("Both provided requested and fallback encoding formats are not supported.") + .d("resolveKey", it->first) + .d("requestedEncoding", encodingFormatToString(requestedEncoding)) + .d("fallbackEncoding", encodingFormatToString(fallbackEncoding))); + } + } + if (!result.empty()) { + std::lock_guard lock(m_encodingFormatMutex); + m_encodingAudioFormats = result; + } else { + ACSDK_ERROR(LX("None of requested encoding audio formats are supported.")); + } + return result; +} + +void AudioInputProcessor::closeAttachmentReaders(attachment::AttachmentReader::ClosePoint closePoint) { + for (auto& it : m_attachmentReaders) { + for (auto& namedReader : it.second) { + if (namedReader && namedReader->reader) { + namedReader->reader->close(closePoint); + } + } + } + m_attachmentReaders.clear(); +} + +bool AudioInputProcessor::isEncodingFormatSupported(avsCommon::utils::AudioFormat::Encoding encodingFormat) const { + if (encodingFormat == AudioFormat::Encoding::LPCM || + (m_encoder && m_encoder->getContext() && + encodingFormat == m_encoder->getContext()->getAudioFormat().encoding)) { + return true; + } + return false; +} + +bool AudioInputProcessor::isUsingEncoderLocked() const { + for (auto it = m_encodingAudioFormats.begin(); it != m_encodingAudioFormats.end(); it++) { + if (it->second != AudioFormat::Encoding::LPCM) { + return true; + } + } + return false; +} + +bool AudioInputProcessor::multiStreamsRequestedLocked() const { + return m_encodingAudioFormats.size() > 1; +} + +AudioInputProcessor::EncodingFormatResponse AudioInputProcessor::getEncodingAudioFormats() const { + std::lock_guard lock(m_encodingFormatMutex); + return m_encodingAudioFormats; +} + } // namespace aip } // namespace capabilityAgents } // namespace alexaClientSDK diff --git a/CapabilityAgents/AIP/src/CMakeLists.txt b/CapabilityAgents/AIP/src/CMakeLists.txt index dc3abfa2e8..461688d652 100644 --- a/CapabilityAgents/AIP/src/CMakeLists.txt +++ b/CapabilityAgents/AIP/src/CMakeLists.txt @@ -17,7 +17,8 @@ target_link_libraries(AIP AFML DeviceSettings SpeechEncoder - SystemSoundPlayer) + SystemSoundPlayer + acsdkNotifier) # install target asdk_install() diff --git a/CapabilityAgents/AIP/test/AudioInputProcessorTest.cpp b/CapabilityAgents/AIP/test/AudioInputProcessorTest.cpp index 320cf19dea..856962535e 100644 --- a/CapabilityAgents/AIP/test/AudioInputProcessorTest.cpp +++ b/CapabilityAgents/AIP/test/AudioInputProcessorTest.cpp @@ -27,7 +27,10 @@ #include #include +#include +#include #include +#include #include #include #include @@ -139,7 +142,7 @@ static const size_t SDS_WORDS = PREROLL_WORDS + WAKEWORD_WORDS + 1000; static const size_t PATTERN_WORDS = SDS_WORDS / 2; /// Maximum number of readers to support in the SDS circular buffer. -static const size_t SDS_MAXREADERS = 3; +static const size_t SDS_MAXREADERS = 10; /// Boolean value to indicate an AudioProvider is always readable. static const bool ALWAYS_READABLE = true; @@ -186,8 +189,14 @@ static const std::string ASR_PROFILE_KEY = "profile"; /// JSON key for the audio format field of a recognize event. static const std::string AUDIO_FORMAT_KEY = "format"; +/// JSON value for audio format OPUS +static const std::string AUDIO_FORMAT_OPUS = "OPUS"; + +/// JSON value for audio format LPCM +static const std::string AUDIO_FORMAT_LPCM = "AUDIO_L16_RATE_16000_CHANNELS_1"; + /// Accepted JSON values for a recognize event's audio format. -static const std::unordered_set AUDIO_FORMAT_VALUES = {"AUDIO_L16_RATE_16000_CHANNELS_1", "OPUS"}; +static const std::unordered_set AUDIO_FORMAT_VALUES = {AUDIO_FORMAT_LPCM, AUDIO_FORMAT_OPUS}; /// JSON key for the initiator field of a recognize event. static const std::string RECOGNIZE_INITIATOR_KEY = "initiator"; @@ -287,6 +296,10 @@ static const std::string WAKEWORDS_PAYLOAD_KEY = "wakeWords"; /// A list of test supported wake words. static const std::set SUPPORTED_WAKE_WORDS = {"ALEXA", "ECHO"}; +/// A list of test supported locale specific wakewords. +static const std::map>> SUPPORTED_LOCALE_SPECIFIC_WAKEWORDS = { + {"en-US", {{"COMPUTER"}}}}; + /// A list of test supported locales. static const std::set SUPPORTED_LOCALES = {"en-CA", "en-US"}; @@ -302,6 +315,23 @@ static const ContextRequestToken CONTEXT_REQUEST_TOKEN{1}; /// Component name for power resource management. static const std::string COMPONENT_NAME("AudioInputProcessor"); +/// Request to configure AIP to produce multiple audio streams with different encoding formats +static const AudioInputProcessor::EncodingFormatRequest ENCODING_FORMAT_REQ = { + {"CLOUD", {avsCommon::utils::AudioFormat::Encoding::OPUS, avsCommon::utils::AudioFormat::Encoding::LPCM}}, + {"LOCAL", {avsCommon::utils::AudioFormat::Encoding::LPCM, avsCommon::utils::AudioFormat::Encoding::LPCM}}, + {"LOCAL2", {avsCommon::utils::AudioFormat::Encoding::LPCM, avsCommon::utils::AudioFormat::Encoding::LPCM}}}; + +/// Response for the request to AIP to produce multiple audio streams +static const AudioInputProcessor::EncodingFormatResponse ENCODING_FORMAT_RSP = { + {"CLOUD", avsCommon::utils::AudioFormat::Encoding::OPUS}, + {"LOCAL", avsCommon::utils::AudioFormat::Encoding::LPCM}, + {"LOCAL2", avsCommon::utils::AudioFormat::Encoding::LPCM}}; + +/// Expected resolve key to encoding format strings +static const std::map EXPECTED_ENCODING_FORMATS = {{"CLOUD", AUDIO_FORMAT_OPUS}, + {"LOCAL", AUDIO_FORMAT_LPCM}, + {"LOCAL2", AUDIO_FORMAT_LPCM}}; + /// Utility function to parse a JSON document. static rapidjson::Document parseJson(const std::string& json) { rapidjson::Document document; @@ -315,6 +345,9 @@ static rapidjson::Document parseJson(const std::string& json) { /// Utility function to look up a JSON string in a container. static std::string getJsonString(const rapidjson::Value& container, const std::string& key) { auto member = container.FindMember(key); + if (member == container.MemberEnd()) { + return ""; + } EXPECT_TRUE(member->value.IsString()); if (!member->value.IsString()) { return ""; @@ -349,7 +382,8 @@ class RecognizeEvent { avsCommon::avs::AudioInputStream::Index keywordEnd = AudioInputProcessor::INVALID_INDEX, std::string keyword = "", std::shared_ptr avsInitiator = nullptr, - const std::shared_ptr> KWDMetadata = nullptr); + const std::shared_ptr> KWDMetadata = nullptr, + std::string expectedFormat = ""); /** * This function sends a recognize event using the provided @c AudioInputProcessor and the recognize parameters @@ -402,14 +436,15 @@ class RecognizeEvent { void verifyMessage( std::shared_ptr request, const std::vector& pattern, - const std::string& dialogRequestId); + const std::string& dialogRequestId, + const std::string& expectedFormat = ""); /** * Accessor function to get the attachment reader for a verified message. * * @return the attachment reader for a verified message. */ - std::shared_ptr getReader(); + std::vector> getNamedReaders(); private: /// The audio provider to use for this recognize event. @@ -430,11 +465,14 @@ class RecognizeEvent { /// The initiator that is passed from AVS in a preceding ExpectSpeech. std::shared_ptr m_avsInitiator; - /// The user voice attachment reader saved by a call to @c verifyMessage(). - std::shared_ptr m_reader; + /// The user voice attachment readers saved by a call to @c verifyMessage(). + std::vector> m_readers; /// The wake word engine metadata attachment reader saved by a call to @c verifyMessage(). std::shared_ptr> m_KWDMetadata; + + /// Expected audio encoding format + std::string m_expectedFormat; }; RecognizeEvent::RecognizeEvent( @@ -444,14 +482,16 @@ RecognizeEvent::RecognizeEvent( avsCommon::avs::AudioInputStream::Index keywordEnd, std::string keyword, std::shared_ptr avsInitiator, - const std::shared_ptr> KWDMetadata) : + const std::shared_ptr> KWDMetadata, + std::string expectedFormat) : m_audioProvider{audioProvider}, m_initiator{initiator}, m_begin{begin}, m_keywordEnd{keywordEnd}, m_keyword{keyword}, m_avsInitiator{avsInitiator}, - m_KWDMetadata{KWDMetadata} { + m_KWDMetadata{KWDMetadata}, + m_expectedFormat(expectedFormat) { } std::future RecognizeEvent::send(std::shared_ptr audioInputProcessor) { @@ -505,7 +545,8 @@ void RecognizeEvent::verifyMetadata( void RecognizeEvent::verifyMessage( std::shared_ptr request, const std::vector& pattern, - const std::string& dialogRequestId) { + const std::string& dialogRequestId, + const std::string& expectedFormat) { rapidjson::Document document; document.Parse(request->getJsonContent().c_str()); EXPECT_FALSE(document.HasParseError()) @@ -530,12 +571,13 @@ void RecognizeEvent::verifyMessage( std::ostringstream profile; profile << m_audioProvider.profile; - std::ostringstream encodingFormat; - encodingFormat << m_audioProvider.format.encoding; - EXPECT_EQ(getJsonString(payload->value, ASR_PROFILE_KEY), profile.str()); EXPECT_EQ(getJsonString(payload->value, START_OF_SPEECH_TIMESTAMP_FIELD_NAME), START_OF_SPEECH_TIMESTAMP_STR); + if (!expectedFormat.empty()) { + EXPECT_EQ(getJsonString(payload->value, AUDIO_FORMAT_KEY), expectedFormat); + } + EXPECT_FALSE( AUDIO_FORMAT_VALUES.find(getJsonString(payload->value, AUDIO_FORMAT_KEY)) == AUDIO_FORMAT_VALUES.end()); auto initiator = payload->value.FindMember(RECOGNIZE_INITIATOR_KEY); @@ -564,16 +606,17 @@ void RecognizeEvent::verifyMessage( } } - m_reader = request->getAttachmentReader(request->attachmentReadersCount() - 1); - EXPECT_NE(m_reader, nullptr); - EXPECT_EQ(m_reader->name, AUDIO_ATTACHMENT_FIELD_NAME); + auto namedReader = request->getAttachmentReader(request->attachmentReadersCount() - 1); + m_readers.push_back(namedReader); + EXPECT_NE(namedReader, nullptr); + EXPECT_EQ(namedReader->name, AUDIO_ATTACHMENT_FIELD_NAME); std::vector samples(PATTERN_WORDS); size_t samplesRead = 0; auto t0 = std::chrono::steady_clock::now(); do { avsCommon::avs::attachment::AttachmentReader::ReadStatus status; - auto bytesRead = m_reader->reader->read( + auto bytesRead = namedReader->reader->read( samples.data() + samplesRead, (samples.size() - samplesRead) * SDS_WORDSIZE, &status); if (avsCommon::avs::attachment::AttachmentReader::ReadStatus::OK_WOULDBLOCK == status) { std::this_thread::yield(); @@ -583,13 +626,13 @@ void RecognizeEvent::verifyMessage( EXPECT_GT(bytesRead, 0u); EXPECT_EQ(bytesRead % 2, 0u); samplesRead += bytesRead / 2; - } while (samplesRead < samples.size() && t0 - std::chrono::steady_clock::now() < TEST_TIMEOUT); + } while (samplesRead < samples.size() && std::chrono::steady_clock::now() - t0 < TEST_TIMEOUT); EXPECT_EQ(samplesRead, samples.size()); EXPECT_EQ(samples, pattern); } -std::shared_ptr RecognizeEvent::getReader() { - return m_reader->reader; +std::vector> RecognizeEvent::getNamedReaders() { + return m_readers; } /// Class to monitor DialogUXStateAggregator for the @c THINKING state and automatically move it to @c IDLE. @@ -616,7 +659,7 @@ TestDialogUXStateObserver::TestDialogUXStateObserver( void TestDialogUXStateObserver::onDialogUXStateChanged(DialogUXState newState) { if (DialogUXState::THINKING == newState) { - m_aggregator->receive("", ""); + m_aggregator->onRequestProcessingCompleted(); } } @@ -627,6 +670,27 @@ class MockExpectSpeechTimeoutHandler : public avsCommon::sdkInterfaces::ExpectSp bool(std::chrono::milliseconds timeout, const std::function()>& expectSpeechTimedOut)); }; +/// Mock class that implements the SpeechEncoderContext. +class MockEncoderContext : public speechencoder::EncoderContext { +public: + MOCK_METHOD1(init, bool(alexaClientSDK::avsCommon::utils::AudioFormat inputFormat)); + MOCK_METHOD0(getInputFrameSize, size_t()); + MOCK_METHOD0(getOutputFrameSize, size_t()); + MOCK_METHOD0(requiresFullyRead, bool()); + MOCK_METHOD0(getAudioFormat, alexaClientSDK::avsCommon::utils::AudioFormat()); + MOCK_METHOD0(getAVSFormatName, std::string()); + MOCK_METHOD0(start, bool()); + MOCK_METHOD3(processSamples, ssize_t(void* samples, size_t numberOfWords, uint8_t* buffer)); + MOCK_METHOD0(close, void()); +}; + +/// Mock class that implements the @c CapabilityConfigurationChangeObserverInterface. +class MockCapabilityConfigurationChangeObserver + : public avsCommon::sdkInterfaces::CapabilityConfigurationChangeObserverInterface { +public: + MOCK_METHOD1(onConfigurationChanged, void(const avsCommon::avs::CapabilityConfiguration& configuration)); +}; + /// Test harness for @c AudioInputProcessor class. class AudioInputProcessorTest : public ::testing::Test { public: @@ -654,8 +718,10 @@ class AudioInputProcessorTest : public ::testing::Test { /// Enumerates the different points when to pass a stop capture directive to AIP via @c /// AudioInputProcessor::handleDirectiveImmediately()) enum class StopCaptureDirectiveSchedule { - /// Pass a stop capture directive to AIP before the event stream is closed. - BEFORE_EVENT_STREAM_CLOSE, + /// Pass a stop capture directive to AIP before the response code to the Recognize request is receeived. + BEFORE_RESPONSE_STATUS_RECEIVED, + /// Pass a stop capture directive to AIP after the response ccode to the Recognize requet is received. + AFTER_RESPONSE_CODE_RECEIVED, /// Pass a stop capture directive after the event stream is closed. AFTER_EVENT_STREAM_CLOSE, /// Do not pass a stop capture directive. @@ -690,7 +756,9 @@ class AudioInputProcessorTest : public ::testing::Test { std::string keyword = "", RecognizeStopPoint stopPoint = RecognizeStopPoint::NONE, std::shared_ptr avsInitiator = nullptr, - const std::shared_ptr> KWDMetadata = nullptr); + const std::shared_ptr> KWDMetadata = nullptr, + std::string expectedFormat = "", + std::map encodingFormats = {}); /** * Function to call @c AudioInputProcessor::stopCapture() and verify that it succeeds. @@ -768,6 +836,11 @@ class AudioInputProcessorTest : public ::testing::Test { */ bool testRecognizeWithExpectSpeechInitiator(bool withInitiator); + /** + * Function to request AIP to produce multiple audio streams of different encodings for upcoming RecognizeEvent + */ + void testRequestEncodingAudioFormatsSucceeds(); + /** * Function to construct an @c AVSDirective for the specified namespace/name. * @@ -776,7 +849,7 @@ class AudioInputProcessorTest : public ::testing::Test { * @param withInitiator A flag indicating whether the directive should have an initiator. * @return the constructed @c AVSDirective. */ - static std::shared_ptr createAVSDirective( + std::shared_ptr createAVSDirective( const avsCommon::avs::NamespaceAndName& directive, bool withDialogRequestId, bool withInitiator = true); @@ -791,7 +864,7 @@ class AudioInputProcessorTest : public ::testing::Test { * @param document The payload to use. * @return the constructed @c AVSDirective. */ - static std::shared_ptr createAVSDirective( + std::shared_ptr createAVSDirective( const avsCommon::avs::NamespaceAndName& directive, bool withDialogRequestId, bool withInitiator, @@ -830,20 +903,32 @@ class AudioInputProcessorTest : public ::testing::Test { bool testFocusChange(avsCommon::avs::FocusState state, avsCommon::avs::MixingBehavior behavior); /** - * Performs a test to check the AIP correctly transitions to a state after getting notified that the recognize event - * stream has been closed and/or receiving a stop capture directive. + * Performs a test to check the AIP correctly transitions to a state after getting notified of the + * response to the recognize event and/or receiving a stop capture directive. * - * @param eventStreamFinishedStatus The status of the recognize event stream when the stream closes. + * @param responseStatus The response status for the recognize request. * @param stopCaptureSchedule Specify the point when to pass a stop capture directive to AIP. * @param expectedAIPFinalState The expected final state of the AIP. * @param expectFocusReleased If true, it is expected that AIP will release the channel focus in the final state, * and false otherwise. + * @param closeRequest Whether onSendCompleted() should be called to simulate a stream close. */ - void testAIPStateTransitionOnEventFinish( - avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status eventStreamFinishedStatus, + void testAIPStateTransitionOnEventResponse( + avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status responseStatus, StopCaptureDirectiveSchedule stopCaptureSchedule, AudioInputProcessorObserverInterface::State expectedAIPFinalState, - bool expectFocusReleased); + bool expectFocusReleased, + bool closeRequest = true); + + /** + * Function which does encoder specific setup + */ + void setupEncoderTest(); + + /** + * Resets the audio input processor, call after any test. + */ + void resetAudioInputProcessor(); /// The metric recorder. std::shared_ptr m_metricRecorder; @@ -860,6 +945,12 @@ class AudioInputProcessorTest : public ::testing::Test { /// Mock wake word confirmation setting. std::shared_ptr> m_mockWakeWordConfirmation; + /// The mock @c CapabilityconfigurationChangeObserverInterface + std::shared_ptr m_mockCapabilityConfigurationChangeObserver; + + /// The @c CapabilityChangeNotifierInterface + std::shared_ptr m_capabilityChangeNotifier; + /// The locale and wake words settings. std::shared_ptr> m_mockWakeWordSetting; @@ -912,6 +1003,15 @@ class AudioInputProcessorTest : public ::testing::Test { std::vector m_pattern; std::string m_dialogRequestId; + + /// The audio encoder object + std::shared_ptr m_speechEncoder; + + /// The mock @c EncoderContext + std::shared_ptr m_mockEncoderContext; + + /// Message ID in directive + std::string m_messageId; }; void AudioInputProcessorTest::SetUp() { @@ -930,6 +1030,10 @@ void AudioInputProcessorTest::SetUp() { m_mockWakeWordConfirmation = std::make_shared>( settings::getWakeWordConfirmationDefault()); + m_mockCapabilityConfigurationChangeObserver = + std::make_shared>(); + m_capabilityChangeNotifier = std::make_shared(); + m_capabilityChangeNotifier->addObserver(m_mockCapabilityConfigurationChangeObserver); m_mockWakeWordSetting = std::make_shared>(SUPPORTED_WAKE_WORDS); m_mockExceptionEncounteredSender = @@ -976,6 +1080,7 @@ void AudioInputProcessorTest::SetUp() { m_mockAssetsManager, m_mockWakeWordConfirmation, m_mockSpeechConfirmation, + m_capabilityChangeNotifier, m_mockWakeWordSetting, nullptr, *m_audioProvider, @@ -991,9 +1096,59 @@ void AudioInputProcessorTest::SetUp() { // Populate the test pattern with values that correspond to indexes for easy verification. m_pattern.resize(PATTERN_WORDS); std::iota(m_pattern.begin(), m_pattern.end(), 0); + + m_mockEncoderContext = std::make_shared(); + m_speechEncoder = std::make_shared(m_mockEncoderContext); + EXPECT_CALL(*m_mockEncoderContext, init(_)).Times(AtLeast(0)).WillRepeatedly(Return(true)); + EXPECT_CALL(*m_mockEncoderContext, requiresFullyRead()).Times(AtLeast(0)).WillRepeatedly(Return(true)); + EXPECT_CALL(*m_mockEncoderContext, getAVSFormatName()).WillRepeatedly(Return(std::string(AUDIO_FORMAT_OPUS))); + EXPECT_CALL(*m_mockEncoderContext, getInputFrameSize()).WillRepeatedly(Return(1)); + EXPECT_CALL(*m_mockEncoderContext, getOutputFrameSize()).WillRepeatedly(Return(2)); + EXPECT_CALL(*m_mockEncoderContext, processSamples(_, _, _)) + .WillRepeatedly(Invoke([](void* samples, size_t numberOfWords, uint8_t* buffer) { + uint8_t* src = static_cast(samples); + buffer[0] = src[0]; + buffer[1] = src[1]; + return 2; + })); + EXPECT_CALL(*m_mockEncoderContext, getAudioFormat()) + .WillRepeatedly( + Return(avsCommon::utils::AudioFormat{.encoding = avsCommon::utils::AudioFormat::Encoding::OPUS, + .endianness = avsCommon::utils::AudioFormat::Endianness::LITTLE, + .sampleRateHz = 16000, + .sampleSizeInBits = 16, + .numChannels = 1, + .dataSigned = false, + .layout = avsCommon::utils::AudioFormat::Layout::NON_INTERLEAVED})); + EXPECT_CALL(*m_mockEncoderContext, start()).WillRepeatedly(Return(true)); +} + +void AudioInputProcessorTest::setupEncoderTest() { + resetAudioInputProcessor(); + m_audioInputProcessor = AudioInputProcessor::create( + m_mockDirectiveSequencer, + m_mockMessageSender, + m_mockContextManager, + m_mockFocusManager, + m_dialogUXStateAggregator, + m_mockExceptionEncounteredSender, + m_mockUserInactivityMonitor, + m_mockSystemSoundPlayer, + m_mockAssetsManager, + m_mockWakeWordConfirmation, + m_mockSpeechConfirmation, + m_capabilityChangeNotifier, + m_mockWakeWordSetting, + m_speechEncoder, + *m_audioProvider, + m_mockPowerResourceManager, + m_metricRecorder); + ASSERT_NE(m_audioInputProcessor, nullptr); + m_audioInputProcessor->addObserver(m_dialogUXStateAggregator); + m_audioInputProcessor->addObserver(m_mockObserver); } -void AudioInputProcessorTest::TearDown() { +void AudioInputProcessorTest::resetAudioInputProcessor() { if (m_audioInputProcessor) { m_audioInputProcessor->removeObserver(m_dialogUXStateAggregator); EXPECT_CALL(*m_mockFocusManager, releaseChannel(CHANNEL_NAME, _)).Times(AtLeast(0)); @@ -1001,7 +1156,12 @@ void AudioInputProcessorTest::TearDown() { .Times(AtLeast(0)); m_audioInputProcessor->resetState().wait(); } +} + +void AudioInputProcessorTest::TearDown() { + resetAudioInputProcessor(); m_dialogUXStateAggregator->removeObserver(m_dialogUXStateObserver); + m_capabilityChangeNotifier->removeObserver(m_mockCapabilityConfigurationChangeObserver); } bool AudioInputProcessorTest::testRecognizeFails( @@ -1022,7 +1182,9 @@ bool AudioInputProcessorTest::testRecognizeSucceeds( std::string keyword, RecognizeStopPoint stopPoint, std::shared_ptr avsInitiator, - const std::shared_ptr> KWDMetadata) { + const std::shared_ptr> KWDMetadata, + std::string expectedFormat, + std::map expectedFormats) { std::mutex mutex; std::condition_variable conditionVariable; @@ -1042,7 +1204,7 @@ bool AudioInputProcessorTest::testRecognizeSucceeds( contextDocument.Accept(contextWriter); std::string contextJson = contextBuffer.GetString(); m_recognizeEvent = std::make_shared( - audioProvider, initiator, begin, keywordEnd, keyword, avsInitiator, KWDMetadata); + audioProvider, initiator, begin, keywordEnd, keyword, avsInitiator, KWDMetadata, expectedFormat); if (keyword.empty()) { EXPECT_CALL(*m_mockContextManager, getContextWithoutReportableStateProperties(_, _, _)) .WillOnce(InvokeWithoutArgs([this, contextJson, stopPoint] { @@ -1089,9 +1251,27 @@ bool AudioInputProcessorTest::testRecognizeSucceeds( InSequence dummy; EXPECT_CALL(*m_mockMessageSender, sendMessage(_)) .WillOnce(DoAll( - Invoke([this, KWDMetadata](std::shared_ptr request) { - m_recognizeEvent->verifyMetadata(request, KWDMetadata); - m_recognizeEvent->verifyMessage(request, m_pattern, m_mockDirectiveSequencer->getDialogRequestId()); + Invoke([this, KWDMetadata, expectedFormat, expectedFormats]( + std::shared_ptr request) { + if (!expectedFormats.empty()) { + EXPECT_FALSE(request->isResolved()); + for (auto const& it : expectedFormats) { + auto resolveKey = it.first; + auto encodingFormat = it.second; + auto resolvedRequest = request->resolveRequest(resolveKey); + EXPECT_FALSE(resolvedRequest == nullptr); + m_recognizeEvent->verifyMetadata(resolvedRequest, KWDMetadata); + m_recognizeEvent->verifyMessage( + resolvedRequest, + m_pattern, + m_mockDirectiveSequencer->getDialogRequestId(), + encodingFormat); + } + } else { + m_recognizeEvent->verifyMetadata(request, KWDMetadata); + m_recognizeEvent->verifyMessage( + request, m_pattern, m_mockDirectiveSequencer->getDialogRequestId(), expectedFormat); + } }), InvokeWithoutArgs([&] { if (RecognizeStopPoint::AFTER_SEND == stopPoint) { @@ -1468,6 +1648,11 @@ bool AudioInputProcessorTest::testRecognizeWithExpectSpeechInitiator(bool withIn return conditionVariable.wait_for(lock, TEST_TIMEOUT, [&done] { return done; }); } +void AudioInputProcessorTest::testRequestEncodingAudioFormatsSucceeds() { + EXPECT_EQ(m_audioInputProcessor->requestEncodingAudioFormats(ENCODING_FORMAT_REQ), ENCODING_FORMAT_RSP); + EXPECT_EQ(m_audioInputProcessor->getEncodingAudioFormats(), ENCODING_FORMAT_RSP); +} + std::shared_ptr AudioInputProcessorTest::createAVSDirective( const avsCommon::avs::NamespaceAndName& directive, bool withDialogRequestId, @@ -1496,11 +1681,10 @@ std::shared_ptr AudioInputProcessorTest::createAVS bool withInitiator, rapidjson::Document& document, rapidjson::Value& payloadJson) { + m_messageId = avsCommon::utils::uuidGeneration::generateUUID(); + auto header = std::make_shared( - directive.nameSpace, - directive.name, - avsCommon::utils::uuidGeneration::generateUUID(), - avsCommon::utils::uuidGeneration::generateUUID()); + directive.nameSpace, directive.name, m_messageId, avsCommon::utils::uuidGeneration::generateUUID()); rapidjson::Value directiveJson(rapidjson::kObjectType); rapidjson::Value headerJson(rapidjson::kObjectType); @@ -1564,6 +1748,7 @@ void AudioInputProcessorTest::removeDefaultAudioProvider() { m_mockAssetsManager, m_mockWakeWordConfirmation, m_mockSpeechConfirmation, + m_capabilityChangeNotifier, m_mockWakeWordSetting, nullptr, AudioProvider::null(), @@ -1589,6 +1774,7 @@ void AudioInputProcessorTest::makeDefaultAudioProviderNotAlwaysReadable() { m_mockAssetsManager, m_mockWakeWordConfirmation, m_mockSpeechConfirmation, + m_capabilityChangeNotifier, m_mockWakeWordSetting, nullptr, *m_audioProvider, @@ -1613,6 +1799,7 @@ void AudioInputProcessorTest::addExpectSpeechTimeoutHandler() { m_mockAssetsManager, m_mockWakeWordConfirmation, m_mockSpeechConfirmation, + m_capabilityChangeNotifier, m_mockWakeWordSetting, nullptr, *m_audioProvider, @@ -1651,19 +1838,24 @@ bool AudioInputProcessorTest::testFocusChange( return conditionVariable.wait_for(lock, TEST_TIMEOUT, [&done] { return done; }); } -void AudioInputProcessorTest::testAIPStateTransitionOnEventFinish( - avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status eventStreamFinishedStatus, +void AudioInputProcessorTest::testAIPStateTransitionOnEventResponse( + MessageRequestObserverInterface::Status responseStatus, StopCaptureDirectiveSchedule stopCaptureSchedule, AudioInputProcessorObserverInterface::State expectedAIPFinalState, - bool expectFocusReleased) { + bool expectFocusReleased, + bool closeRequest) { // Simulate tap to talk and start recognizing. ASSERT_TRUE(testRecognizeSucceeds(*m_audioProvider, Initiator::TAP, 0)); // Expect some AIP transient states. EXPECT_CALL(*m_mockObserver, onStateChanged(_)).Times(AtLeast(0)); - // Expected final state. - EXPECT_CALL(*m_mockObserver, onStateChanged(expectedAIPFinalState)).Times(1); + // Expected final state. If the expected state is RECOGNIZING, that means we expect no state transition. + if (AudioInputProcessorObserverInterface::State::RECOGNIZING == expectedAIPFinalState) { + EXPECT_CALL(*m_mockObserver, onStateChanged(expectedAIPFinalState)).Times(0); + } else { + EXPECT_CALL(*m_mockObserver, onStateChanged(expectedAIPFinalState)).Times(1); + } if (expectFocusReleased) { EXPECT_CALL(*m_mockFocusManager, releaseChannel(CHANNEL_NAME, _)); @@ -1671,11 +1863,22 @@ void AudioInputProcessorTest::testAIPStateTransitionOnEventFinish( auto avsDirective = createAVSDirective(STOP_CAPTURE, true); - if (StopCaptureDirectiveSchedule::BEFORE_EVENT_STREAM_CLOSE == stopCaptureSchedule) { + if (StopCaptureDirectiveSchedule::BEFORE_RESPONSE_STATUS_RECEIVED == stopCaptureSchedule) { + m_audioInputProcessor->handleDirectiveImmediately(avsDirective); + } + + if (MessageRequestObserverInterface::Status::TIMEDOUT != responseStatus && + MessageRequestObserverInterface::Status::CANCELED != responseStatus) { + m_audioInputProcessor->onResponseStatusReceived(responseStatus); + } + + if (StopCaptureDirectiveSchedule::AFTER_RESPONSE_CODE_RECEIVED == stopCaptureSchedule) { m_audioInputProcessor->handleDirectiveImmediately(avsDirective); } - m_audioInputProcessor->onSendCompleted(eventStreamFinishedStatus); + if (closeRequest) { + m_audioInputProcessor->onSendCompleted(responseStatus); + } if (StopCaptureDirectiveSchedule::AFTER_EVENT_STREAM_CLOSE == stopCaptureSchedule) { m_audioInputProcessor->handleDirectiveImmediately(avsDirective); @@ -1697,6 +1900,7 @@ TEST_F(AudioInputProcessorTest, test_createWithoutDirectiveSequencer) { m_mockAssetsManager, m_mockWakeWordConfirmation, m_mockSpeechConfirmation, + m_capabilityChangeNotifier, m_mockWakeWordSetting, nullptr, *m_audioProvider, @@ -1720,6 +1924,7 @@ TEST_F(AudioInputProcessorTest, test_createWithoutMessageSender) { m_mockAssetsManager, m_mockWakeWordConfirmation, m_mockSpeechConfirmation, + m_capabilityChangeNotifier, m_mockWakeWordSetting, nullptr, *m_audioProvider, @@ -1743,6 +1948,7 @@ TEST_F(AudioInputProcessorTest, test_createWithoutContextManager) { m_mockAssetsManager, m_mockWakeWordConfirmation, m_mockSpeechConfirmation, + m_capabilityChangeNotifier, m_mockWakeWordSetting, nullptr, *m_audioProvider, @@ -1766,6 +1972,7 @@ TEST_F(AudioInputProcessorTest, test_createWithoutFocusManager) { m_mockAssetsManager, m_mockWakeWordConfirmation, m_mockSpeechConfirmation, + m_capabilityChangeNotifier, m_mockWakeWordSetting, nullptr, *m_audioProvider, @@ -1789,6 +1996,7 @@ TEST_F(AudioInputProcessorTest, test_createWithoutStateAggregator) { m_mockAssetsManager, m_mockWakeWordConfirmation, m_mockSpeechConfirmation, + m_capabilityChangeNotifier, m_mockWakeWordSetting, nullptr, *m_audioProvider, @@ -1815,6 +2023,7 @@ TEST_F(AudioInputProcessorTest, test_createWithoutExceptionSender) { m_mockAssetsManager, m_mockWakeWordConfirmation, m_mockSpeechConfirmation, + m_capabilityChangeNotifier, m_mockWakeWordSetting, nullptr, *m_audioProvider, @@ -1841,6 +2050,7 @@ TEST_F(AudioInputProcessorTest, test_createWithoutUserInactivityMonitor) { m_mockAssetsManager, m_mockWakeWordConfirmation, m_mockSpeechConfirmation, + m_capabilityChangeNotifier, m_mockWakeWordSetting, nullptr, *m_audioProvider, @@ -1849,6 +2059,30 @@ TEST_F(AudioInputProcessorTest, test_createWithoutUserInactivityMonitor) { EXPECT_EQ(m_audioInputProcessor, nullptr); } +/// Function to verify that @c AudioInputProcessor::create() errors out with an invalid @c CapabilityChangeNotifier. +TEST_F(AudioInputProcessorTest, test_createWithoutCapabilityChangeNotifier) { + m_audioInputProcessor->removeObserver(m_dialogUXStateAggregator); + m_audioInputProcessor = AudioInputProcessor::create( + m_mockDirectiveSequencer, + m_mockMessageSender, + m_mockContextManager, + m_mockFocusManager, + m_dialogUXStateAggregator, + m_mockExceptionEncounteredSender, + m_mockUserInactivityMonitor, + m_mockSystemSoundPlayer, + m_mockAssetsManager, + m_mockWakeWordConfirmation, + m_mockSpeechConfirmation, + nullptr, + m_mockWakeWordSetting, + nullptr, + AudioProvider::null(), + m_mockPowerResourceManager, + m_metricRecorder); + EXPECT_EQ(m_audioInputProcessor, nullptr); +} + /// Function to verify that @c AudioInputProcessor::create() succeeds with a null @c AudioProvider. TEST_F(AudioInputProcessorTest, test_createWithoutAudioProvider) { m_audioInputProcessor->removeObserver(m_dialogUXStateAggregator); @@ -1864,6 +2098,7 @@ TEST_F(AudioInputProcessorTest, test_createWithoutAudioProvider) { m_mockAssetsManager, m_mockWakeWordConfirmation, m_mockSpeechConfirmation, + m_capabilityChangeNotifier, m_mockWakeWordSetting, nullptr, AudioProvider::null(), @@ -1887,6 +2122,7 @@ TEST_F(AudioInputProcessorTest, test_createWithoutPowerResourceManager) { m_mockAssetsManager, m_mockWakeWordConfirmation, m_mockSpeechConfirmation, + m_capabilityChangeNotifier, m_mockWakeWordSetting, nullptr, AudioProvider::null(), @@ -1910,6 +2146,7 @@ TEST_F(AudioInputProcessorTest, test_createWithoutMetricRecorder) { m_mockAssetsManager, m_mockWakeWordConfirmation, m_mockSpeechConfirmation, + m_capabilityChangeNotifier, m_mockWakeWordSetting, nullptr, AudioProvider::null(), @@ -2059,6 +2296,72 @@ TEST_F(AudioInputProcessorTest, test_recognizeFarField) { ASSERT_TRUE(testRecognizeSucceeds(audioProvider, Initiator::TAP)); } +/// Test if @c AudioInputProcessor::recognize() works with an audio encoder +TEST_F(AudioInputProcessorTest, test_recognizeFarFieldWithEncoder) { + setupEncoderTest(); + auto audioProvider = *m_audioProvider; + audioProvider.profile = ASRProfile::FAR_FIELD; + ASSERT_TRUE(testRecognizeSucceeds( + audioProvider, + Initiator::TAP, + AudioInputProcessor::INVALID_INDEX, + AudioInputProcessor::INVALID_INDEX, + "", + RecognizeStopPoint::NONE, + nullptr, + nullptr, + AUDIO_FORMAT_OPUS)); +} + +/// Test if @c AudioInputProcessor::recognize() works with an audio encoder +TEST_F(AudioInputProcessorTest, test_recognizeFarFieldWithEncoderDisabled) { + setupEncoderTest(); + auto audioProvider = *m_audioProvider; + audioProvider.profile = ASRProfile::FAR_FIELD; + m_audioInputProcessor->setEncodingAudioFormat(avsCommon::utils::AudioFormat::Encoding::LPCM); + ASSERT_TRUE(testRecognizeSucceeds( + audioProvider, + Initiator::TAP, + AudioInputProcessor::INVALID_INDEX, + AudioInputProcessor::INVALID_INDEX, + "", + RecognizeStopPoint::NONE, + nullptr, + nullptr, + AUDIO_FORMAT_LPCM)); +} + +/// Test if @c AudioInputProcessor::recognize() works with an audio encoder +TEST_F(AudioInputProcessorTest, test_recognizeFarFieldWithEncoderReEnabled) { + setupEncoderTest(); + auto audioProvider = *m_audioProvider; + audioProvider.profile = ASRProfile::FAR_FIELD; + + m_audioInputProcessor->setEncodingAudioFormat(avsCommon::utils::AudioFormat::Encoding::LPCM); + ASSERT_TRUE(testRecognizeSucceeds( + audioProvider, + Initiator::TAP, + AudioInputProcessor::INVALID_INDEX, + AudioInputProcessor::INVALID_INDEX, + "", + RecognizeStopPoint::NONE, + nullptr, + nullptr, + AUDIO_FORMAT_LPCM)); + + m_audioInputProcessor->setEncodingAudioFormat(avsCommon::utils::AudioFormat::Encoding::OPUS); + ASSERT_TRUE(testRecognizeSucceeds( + audioProvider, + Initiator::TAP, + AudioInputProcessor::INVALID_INDEX, + AudioInputProcessor::INVALID_INDEX, + "", + RecognizeStopPoint::NONE, + nullptr, + nullptr, + AUDIO_FORMAT_OPUS)); +} + /// This function verifies that @c AudioInputProcessor::recognize() works in @c State::EXPECTING_SPEECH. TEST_F(AudioInputProcessorTest, test_recognizeWhileExpectingSpeech) { removeDefaultAudioProvider(); @@ -2210,10 +2513,12 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureWhenRecognizing) { ASSERT_TRUE(testRecognizeSucceeds(*m_audioProvider, Initiator::TAP, 0)); ASSERT_TRUE(testStopCaptureSucceeds()); - auto readStatus = avsCommon::avs::attachment::AttachmentReader::ReadStatus::OK; - std::vector buf(SDS_WORDS * SDS_WORDSIZE); - EXPECT_EQ(m_recognizeEvent->getReader()->read(buf.data(), buf.size(), &readStatus), 0U); - ASSERT_EQ(readStatus, avsCommon::avs::attachment::AttachmentReader::ReadStatus::CLOSED); + for (auto& namedReader : m_recognizeEvent->getNamedReaders()) { + auto readStatus = avsCommon::avs::attachment::AttachmentReader::ReadStatus::OK; + std::vector buf(SDS_WORDS * SDS_WORDSIZE); + EXPECT_EQ(namedReader->reader->read(buf.data(), buf.size(), &readStatus), 0U); + ASSERT_EQ(readStatus, avsCommon::avs::attachment::AttachmentReader::ReadStatus::CLOSED); + } } /* @@ -2224,10 +2529,12 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureWhenRecognizingFollowByStopCaptu ASSERT_TRUE(testRecognizeSucceeds(*m_audioProvider, Initiator::TAP, 0)); ASSERT_TRUE(testStopCaptureSucceeds()); - auto readStatus = avsCommon::avs::attachment::AttachmentReader::ReadStatus::OK; - std::vector buf(SDS_WORDS * SDS_WORDSIZE); - EXPECT_EQ(m_recognizeEvent->getReader()->read(buf.data(), buf.size(), &readStatus), 0U); - ASSERT_EQ(readStatus, avsCommon::avs::attachment::AttachmentReader::ReadStatus::CLOSED); + for (auto& namedReader : m_recognizeEvent->getNamedReaders()) { + auto readStatus = avsCommon::avs::attachment::AttachmentReader::ReadStatus::OK; + std::vector buf(SDS_WORDS * SDS_WORDSIZE); + EXPECT_EQ(namedReader->reader->read(buf.data(), buf.size(), &readStatus), 0U); + ASSERT_EQ(readStatus, avsCommon::avs::attachment::AttachmentReader::ReadStatus::CLOSED); + } std::mutex mutex; std::condition_variable conditionVariable; @@ -2535,15 +2842,54 @@ TEST_F(AudioInputProcessorTest, test_recognizeInvalidWakeWord) { testRecognizeFails(*m_audioProvider, Initiator::WAKEWORD, begin, end, AudioInputProcessor::KEYWORD_TEXT_STOP)); } +/** + * This function verifies that @c AudioInputProcessor state will continue listening when the recognize event stream + * has received SUCCESS but not yet closed. + */ +TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamSuccessBeforeClose) { + testAIPStateTransitionOnEventResponse( + avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS, + StopCaptureDirectiveSchedule::NONE, + AudioInputProcessorObserverInterface::State::RECOGNIZING, + false, + false); +} + /** * This function verifies that @c AudioInputProcessor state will stop listening when the recognize event stream has been * successfully sent. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamSuccess) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS, StopCaptureDirectiveSchedule::NONE, - AudioInputProcessorObserverInterface::State::BUSY, + AudioInputProcessorObserverInterface::State::IDLE, + true); +} + +/** + * This function verifies that @c AudioInputProcessor state will stop listening when the recognize event stream + * has received SUCCESS_NO_CONTENT but the stream has not yet closed. + */ +TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamSuccessNoContentBeforeClose) { + testAIPStateTransitionOnEventResponse( + avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS_NO_CONTENT, + StopCaptureDirectiveSchedule::NONE, + AudioInputProcessorObserverInterface::State::IDLE, + false, + false); +} + +/** + * This function verifies that @c AudioInputProcessor state will stop listening when the recognize event stream + * has received PENDING but the stream has not yet closed. + */ +TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamPendingBeforeClose) { + testAIPStateTransitionOnEventResponse( + avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::PENDING, + StopCaptureDirectiveSchedule::NONE, + AudioInputProcessorObserverInterface::State::IDLE, + false, false); } @@ -2552,7 +2898,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamSuccess) { * successfully sent but received no HTTP/2 content. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamSuccessNoContent) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS_NO_CONTENT, StopCaptureDirectiveSchedule::NONE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2564,7 +2910,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamSuccessNoContent) { * been sent due to connection to AVS has been severed. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamSuccessNotConnected) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::NOT_CONNECTED, StopCaptureDirectiveSchedule::NONE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2576,7 +2922,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamSuccessNotConnected) { * been sent due to AVS is not synchronized. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamNotSynchronized) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::NOT_SYNCHRONIZED, StopCaptureDirectiveSchedule::NONE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2588,7 +2934,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamNotSynchronized) { * been sent due to an internal error within ACL. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamInternalrror) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::INTERNAL_ERROR, StopCaptureDirectiveSchedule::NONE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2600,7 +2946,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamInternalrror) { * been sent due to an underlying protocol error. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamProtocolError) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::PROTOCOL_ERROR, StopCaptureDirectiveSchedule::NONE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2612,7 +2958,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamProtocolError) { * been sent due to an internal error on the server which sends code 500. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamServerInternalError) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2, StopCaptureDirectiveSchedule::NONE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2624,7 +2970,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamServerInternalError) { * been sent due to server refusing the request. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamRefused) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::REFUSED, StopCaptureDirectiveSchedule::NONE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2636,7 +2982,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamRefused) { * been sent due to server canceling it before the transmission completed. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamCanceled) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::CANCELED, StopCaptureDirectiveSchedule::NONE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2648,7 +2994,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamCanceled) { * been sent due to excessive load on the server. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamThrottled) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::THROTTLED, StopCaptureDirectiveSchedule::NONE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2660,7 +3006,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamThrottled) { * been sent due to the access credentials provided to ACL were invalid. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamInvalidAuth) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::INVALID_AUTH, StopCaptureDirectiveSchedule::NONE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2672,7 +3018,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamInvalidAuth) { * been sent due to invalid request sent by the user. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamBadRequest) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::BAD_REQUEST, StopCaptureDirectiveSchedule::NONE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2684,7 +3030,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamBadRequest) { * been sent due to unknown server error. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamUnknownServerError) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SERVER_OTHER_ERROR, StopCaptureDirectiveSchedule::NONE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2696,9 +3042,9 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamUnknownServerError) { * recognize event stream has been successfully sent. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamSuccess) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS, - StopCaptureDirectiveSchedule::BEFORE_EVENT_STREAM_CLOSE, + StopCaptureDirectiveSchedule::BEFORE_RESPONSE_STATUS_RECEIVED, AudioInputProcessorObserverInterface::State::BUSY, false); } @@ -2708,9 +3054,9 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamSuccess) { * recognize event stream has been successfully sent but received no HTTP/2 content. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamSuccessNoContent) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS_NO_CONTENT, - StopCaptureDirectiveSchedule::BEFORE_EVENT_STREAM_CLOSE, + StopCaptureDirectiveSchedule::BEFORE_RESPONSE_STATUS_RECEIVED, AudioInputProcessorObserverInterface::State::BUSY, false); } @@ -2720,9 +3066,9 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamSuccessNoCon * recognize event stream has not been sent due to connection to AVS has been severed. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamSuccessNotConnected) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::NOT_CONNECTED, - StopCaptureDirectiveSchedule::BEFORE_EVENT_STREAM_CLOSE, + StopCaptureDirectiveSchedule::BEFORE_RESPONSE_STATUS_RECEIVED, AudioInputProcessorObserverInterface::State::IDLE, true); } @@ -2732,9 +3078,9 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamSuccessNotCo * recognize event stream has not been sent due to AVS is not synchronized. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamNotSynchronized) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::NOT_SYNCHRONIZED, - StopCaptureDirectiveSchedule::BEFORE_EVENT_STREAM_CLOSE, + StopCaptureDirectiveSchedule::BEFORE_RESPONSE_STATUS_RECEIVED, AudioInputProcessorObserverInterface::State::IDLE, true); } @@ -2744,9 +3090,9 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamNotSynchroni * recognize event stream has not been sent due to an internal error within ACL. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamInternalrror) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::INTERNAL_ERROR, - StopCaptureDirectiveSchedule::BEFORE_EVENT_STREAM_CLOSE, + StopCaptureDirectiveSchedule::BEFORE_RESPONSE_STATUS_RECEIVED, AudioInputProcessorObserverInterface::State::IDLE, true); } @@ -2756,9 +3102,9 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamInternalrror * recognize event stream has not been sent due to an underlying protocol error. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamProtocolError) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::PROTOCOL_ERROR, - StopCaptureDirectiveSchedule::BEFORE_EVENT_STREAM_CLOSE, + StopCaptureDirectiveSchedule::BEFORE_RESPONSE_STATUS_RECEIVED, AudioInputProcessorObserverInterface::State::IDLE, true); } @@ -2768,9 +3114,9 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamProtocolErro * recognize event stream has not been sent due to an internal error on the server which sends code 500. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamServerInternalError) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2, - StopCaptureDirectiveSchedule::BEFORE_EVENT_STREAM_CLOSE, + StopCaptureDirectiveSchedule::BEFORE_RESPONSE_STATUS_RECEIVED, AudioInputProcessorObserverInterface::State::IDLE, true); } @@ -2780,9 +3126,9 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamServerIntern * recognize event stream has not been sent due to server refusing the request. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamRefused) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::REFUSED, - StopCaptureDirectiveSchedule::BEFORE_EVENT_STREAM_CLOSE, + StopCaptureDirectiveSchedule::BEFORE_RESPONSE_STATUS_RECEIVED, AudioInputProcessorObserverInterface::State::IDLE, true); } @@ -2792,9 +3138,9 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamRefused) { * recognize event stream has not been sent due to server canceling it before the transmission completed. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamCanceled) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::CANCELED, - StopCaptureDirectiveSchedule::BEFORE_EVENT_STREAM_CLOSE, + StopCaptureDirectiveSchedule::BEFORE_RESPONSE_STATUS_RECEIVED, AudioInputProcessorObserverInterface::State::IDLE, true); } @@ -2804,9 +3150,9 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamCanceled) { * recognize event stream has not been sent due to excessive load on the server. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamThrottled) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::THROTTLED, - StopCaptureDirectiveSchedule::BEFORE_EVENT_STREAM_CLOSE, + StopCaptureDirectiveSchedule::BEFORE_RESPONSE_STATUS_RECEIVED, AudioInputProcessorObserverInterface::State::IDLE, true); } @@ -2816,9 +3162,9 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamThrottled) { * recognize event stream has not been sent due to the access credentials provided to ACL were invalid. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamInvalidAuth) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::INVALID_AUTH, - StopCaptureDirectiveSchedule::BEFORE_EVENT_STREAM_CLOSE, + StopCaptureDirectiveSchedule::BEFORE_RESPONSE_STATUS_RECEIVED, AudioInputProcessorObserverInterface::State::IDLE, true); } @@ -2828,9 +3174,9 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamInvalidAuth) * recognize event stream has not been sent due to invalid request sent by the user. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamBadRequest) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::BAD_REQUEST, - StopCaptureDirectiveSchedule::BEFORE_EVENT_STREAM_CLOSE, + StopCaptureDirectiveSchedule::BEFORE_RESPONSE_STATUS_RECEIVED, AudioInputProcessorObserverInterface::State::IDLE, true); } @@ -2840,31 +3186,65 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamBadRequest) * recognize event stream has not been sent due to unknown server error. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnDirectiveAndStreamUnknownServerError) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SERVER_OTHER_ERROR, - StopCaptureDirectiveSchedule::BEFORE_EVENT_STREAM_CLOSE, + StopCaptureDirectiveSchedule::BEFORE_RESPONSE_STATUS_RECEIVED, AudioInputProcessorObserverInterface::State::IDLE, true); } /** - * This function verifies that @c AudioInputProcessor state is correct after the recognize event stream has been - * successfully sent and a stop capture directive is received. + * This function verifies that @c AudioInputProcessor state is correct after the recognize event stream has received + * SUCCESS but is not yet closed and a stop capture directive has been received. */ -TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamSuccessAndDirective) { - testAIPStateTransitionOnEventFinish( +TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamSuccessAndDirectiveBeforeClose) { + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS, - StopCaptureDirectiveSchedule::AFTER_EVENT_STREAM_CLOSE, + StopCaptureDirectiveSchedule::AFTER_RESPONSE_CODE_RECEIVED, AudioInputProcessorObserverInterface::State::BUSY, + false, false); } +/** + * This function verifies that @c AudioInputProcessor state is correct after the recognize event stream has been + * successfully sent and a stop capture directive is received. Also make sure setCompleted() is called on the + * directive. + */ +TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamSuccessAndDirective) { + // Simulate tap to talk and start recognizing. + ASSERT_TRUE(testRecognizeSucceeds(*m_audioProvider, Initiator::TAP, 0)); + + // Expect some AIP transient states. + EXPECT_CALL(*m_mockObserver, onStateChanged(_)).Times(AtLeast(0)); + + // Expect final state to be IDLE. + EXPECT_CALL(*m_mockObserver, onStateChanged(AudioInputProcessorObserverInterface::State::IDLE)).Times(1); + + // Expect channel to be released + EXPECT_CALL(*m_mockFocusManager, releaseChannel(CHANNEL_NAME, _)); + + auto avsDirective = createAVSDirective(STOP_CAPTURE, true); + auto result = avsCommon::utils::memory::make_unique(); + + // Expect setCompleted to be called for the result of the directive. + EXPECT_CALL(*result, setCompleted()); + + m_audioInputProcessor->onResponseStatusReceived( + avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS); + + m_audioInputProcessor->onSendCompleted(avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS); + + m_audioInputProcessor->CapabilityAgent::preHandleDirective(avsDirective, std::move(result)); + m_audioInputProcessor->CapabilityAgent::handleDirective(m_messageId); +} + /** * This function verifies that @c AudioInputProcessor state is correct after the recognize event stream has been * successfully sent but received no HTTP/2 content and a stop capture directive is received. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamSuccessNoContentAndDirective) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS_NO_CONTENT, StopCaptureDirectiveSchedule::AFTER_EVENT_STREAM_CLOSE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2876,7 +3256,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamSuccessNoContentAndDirec * been sent due to connection to AVS has been severed and a stop capture directive is received. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamSuccessNotConnectedAndDirective) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::NOT_CONNECTED, StopCaptureDirectiveSchedule::AFTER_EVENT_STREAM_CLOSE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2888,7 +3268,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamSuccessNotConnectedAndDi * been sent due to AVS is not synchronized and a stop capture directive is received. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamNotSynchronizedAndDirective) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::NOT_SYNCHRONIZED, StopCaptureDirectiveSchedule::AFTER_EVENT_STREAM_CLOSE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2900,7 +3280,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamNotSynchronizedAndDirect * been sent due to an internal error within ACL and a stop capture directive is received. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamInternalrrorAndDirective) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::INTERNAL_ERROR, StopCaptureDirectiveSchedule::AFTER_EVENT_STREAM_CLOSE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2912,7 +3292,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamInternalrrorAndDirective * been sent due to an underlying protocol error and a stop capture directive is received. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamProtocolErrorAndDirective) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::PROTOCOL_ERROR, StopCaptureDirectiveSchedule::AFTER_EVENT_STREAM_CLOSE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2924,7 +3304,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamProtocolErrorAndDirectiv * been sent due to an internal error on the server which sends code 500 and a stop capture directive is received. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamServerInternalErrorAndDirective) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2, StopCaptureDirectiveSchedule::AFTER_EVENT_STREAM_CLOSE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2936,7 +3316,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamServerInternalErrorAndDi * been sent due to server refusing the request and a stop capture directive is received. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamRefusedAndDirective) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::REFUSED, StopCaptureDirectiveSchedule::AFTER_EVENT_STREAM_CLOSE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2948,7 +3328,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamRefusedAndDirective) { * been sent due to server canceling it before the transmission completed and a stop capture directive is received. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamCanceledAndDirective) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::CANCELED, StopCaptureDirectiveSchedule::AFTER_EVENT_STREAM_CLOSE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2960,7 +3340,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamCanceledAndDirective) { * been sent due to excessive load on the server and a stop capture directive is received. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamThrottledAndDirective) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::THROTTLED, StopCaptureDirectiveSchedule::AFTER_EVENT_STREAM_CLOSE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2972,7 +3352,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamThrottledAndDirective) { * been sent due to the access credentials provided to ACL were invalid and a stop capture directive is received. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamInvalidAuthAndDirective) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::INVALID_AUTH, StopCaptureDirectiveSchedule::AFTER_EVENT_STREAM_CLOSE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2984,7 +3364,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamInvalidAuthAndDirective) * been sent due to invalid request sent by the user and a stop capture directive is received. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamBadRequestAndDirective) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::BAD_REQUEST, StopCaptureDirectiveSchedule::AFTER_EVENT_STREAM_CLOSE, AudioInputProcessorObserverInterface::State::IDLE, @@ -2996,7 +3376,7 @@ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamBadRequestAndDirective) * been sent due to unknown server error and a stop capture directive is received. */ TEST_F(AudioInputProcessorTest, test_stopCaptureOnStreamUnknownServerErrorAndDirective) { - testAIPStateTransitionOnEventFinish( + testAIPStateTransitionOnEventResponse( avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SERVER_OTHER_ERROR, StopCaptureDirectiveSchedule::AFTER_EVENT_STREAM_CLOSE, AudioInputProcessorObserverInterface::State::IDLE, @@ -3177,7 +3557,333 @@ TEST_F(AudioInputProcessorTest, test_publishedCapabiltiesContainsSupportedWakeWo EXPECT_THAT(configuration->second, MatchesRegex(R"(.*\[)" + wakeWordString + R"(\].*)")); } +/** + * Test if the @c AudioInputProcessor notifies observers when locale asset is changed and updates its own capability + * configurations. + */ +TEST_F(AudioInputProcessorTest, test_localeAssetsChanged) { + std::string wakeWordString; + for (auto wakeWord : SUPPORTED_WAKE_WORDS) { + if (!wakeWordString.empty()) { + wakeWordString += ","; + } + wakeWordString += "\"" + wakeWord + "\""; + } + + std::string locale = "\"en-US\""; + std::string localeSpecificWakeWordString = "\"COMPUTER\""; + + auto oldWakeWordsRegex = R"(.*\[)" + wakeWordString + R"(\].*)"; + auto newWakeWwordRegex = R"(.*\[)" + wakeWordString + R"(\].*)" + R"(.*\[)" + locale + R"(\].*)" + R"(.*\[)" + + localeSpecificWakeWordString + R"(\].*)"; + + // Check the old SpeechRecognizer capability configuration. + std::unordered_set> caps = + m_audioInputProcessor->getCapabilityConfigurations(); + auto cap = *caps.begin(); + auto configuration = cap->additionalConfigurations.find(CAPABILITY_INTERFACE_CONFIGURATIONS_KEY); + ASSERT_NE(configuration, cap->additionalConfigurations.end()); + EXPECT_THAT(configuration->second, MatchesRegex(oldWakeWordsRegex)); + + // Update the SpeechRecognizer capability configuration. + EXPECT_CALL(*m_mockAssetsManager, getLocaleSpecificWakeWords()) + .WillOnce(InvokeWithoutArgs( + []() -> std::map< + avsCommon::sdkInterfaces::LocaleAssetsManagerInterface::LanguageTag, + avsCommon::sdkInterfaces::LocaleAssetsManagerInterface::WakeWordsSets> { + return SUPPORTED_LOCALE_SPECIFIC_WAKEWORDS; + })); + + EXPECT_CALL(*m_mockCapabilityConfigurationChangeObserver, onConfigurationChanged(_)) + .WillOnce( + Invoke([this, newWakeWwordRegex](const avsCommon::avs::CapabilityConfiguration& capabilityConfiguration) { + auto capapability = + capabilityConfiguration.additionalConfigurations.find(CAPABILITY_INTERFACE_CONFIGURATIONS_KEY); + EXPECT_THAT(capapability->second, MatchesRegex(newWakeWwordRegex)); + + // Retrieve the new SpeechRecognizer capability configuration. + auto newCaps = m_audioInputProcessor->getCapabilityConfigurations(); + auto newCap = *newCaps.begin(); + auto newConfiguration = newCap->additionalConfigurations.find(CAPABILITY_INTERFACE_CONFIGURATIONS_KEY); + ASSERT_NE(newConfiguration, newCap->additionalConfigurations.end()); + EXPECT_THAT(newConfiguration->second, MatchesRegex(newWakeWwordRegex)); + })); + m_audioInputProcessor->onLocaleAssetsChanged(); +} + +/** + * Test if request for multiple encoding formats can be handled correctly. + */ +TEST_F(AudioInputProcessorTest, test_requestEncodingAudioFormatsSuccess) { + setupEncoderTest(); + testRequestEncodingAudioFormatsSucceeds(); +} + +/** + * Test if request for unsupported encoding formats can be handled correctly. + */ +TEST_F(AudioInputProcessorTest, test_requestEncodingAudioFormatsWithFallbackAndUnsupportedFormats) { + setupEncoderTest(); + EXPECT_CALL(*m_mockEncoderContext, getAVSFormatName()).WillRepeatedly(Return(std::string(AUDIO_FORMAT_LPCM))); + EXPECT_CALL(*m_mockEncoderContext, getAudioFormat()) + .WillRepeatedly( + Return(avsCommon::utils::AudioFormat{.encoding = avsCommon::utils::AudioFormat::Encoding::LPCM, + .endianness = avsCommon::utils::AudioFormat::Endianness::LITTLE, + .sampleRateHz = 16000, + .sampleSizeInBits = 16, + .numChannels = 1, + .dataSigned = false, + .layout = avsCommon::utils::AudioFormat::Layout::NON_INTERLEAVED})); + + AudioInputProcessor::EncodingFormatRequest encodingReq = { + {"CLOUD", {avsCommon::utils::AudioFormat::Encoding::OPUS, avsCommon::utils::AudioFormat::Encoding::OPUS}}, + {"LOCAL", {avsCommon::utils::AudioFormat::Encoding::LPCM, avsCommon::utils::AudioFormat::Encoding::LPCM}}}; + AudioInputProcessor::EncodingFormatResponse expectedRsp = { + {"LOCAL", avsCommon::utils::AudioFormat::Encoding::LPCM}}; + + EXPECT_EQ(m_audioInputProcessor->requestEncodingAudioFormats(encodingReq), expectedRsp); + EXPECT_EQ(m_audioInputProcessor->getEncodingAudioFormats(), expectedRsp); +} + +/// This function verifies that when no provided encoding formats are supported, the current encoding formats won't be +/// changed. +TEST_F(AudioInputProcessorTest, test_requestEncodingAudioFormatsFails) { + AudioInputProcessor::EncodingFormatRequest goodReq = { + {"LOCAL", {avsCommon::utils::AudioFormat::Encoding::LPCM, avsCommon::utils::AudioFormat::Encoding::LPCM}}}; + AudioInputProcessor::EncodingFormatRequest badReq = { + {"LOCAL", {avsCommon::utils::AudioFormat::Encoding::OPUS, avsCommon::utils::AudioFormat::Encoding::OPUS}}}; + AudioInputProcessor::EncodingFormatResponse expectedRsp = { + {"LOCAL", avsCommon::utils::AudioFormat::Encoding::LPCM}}; + + EXPECT_EQ(m_audioInputProcessor->requestEncodingAudioFormats(goodReq), expectedRsp); + EXPECT_EQ(m_audioInputProcessor->getEncodingAudioFormats(), expectedRsp); + + EXPECT_TRUE(m_audioInputProcessor->requestEncodingAudioFormats(badReq).empty()); + EXPECT_EQ(m_audioInputProcessor->getEncodingAudioFormats(), expectedRsp); +} + +/// This function verifies that @c AudioInputProcessor::recognize() works with @c Initiator::WAKEWORD and keyword for +/// multiple audio streams +TEST_F(AudioInputProcessorTest, test_recognizeWakewordWithKeywordForMultiStreams) { + setupEncoderTest(); + testRequestEncodingAudioFormatsSucceeds(); + ASSERT_TRUE(testRecognizeSucceeds( + *m_audioProvider, + Initiator::WAKEWORD, + AudioInputProcessor::INVALID_INDEX, + AudioInputProcessor::INVALID_INDEX, + KEYWORD_TEXT, + RecognizeStopPoint::NONE, + nullptr, + nullptr, + "", + EXPECTED_ENCODING_FORMATS)); +} + +/// This function verifies that recognize() works with KWD metadata for multiple audio streams +TEST_F(AudioInputProcessorTest, test_recognizeWakewordWithMetadataForMultipleAudioStreams) { + setupEncoderTest(); + testRequestEncodingAudioFormatsSucceeds(); + + auto metadata = std::make_shared>(); + metadata->assign(KWD_METADATA_EXAMPLE.data(), KWD_METADATA_EXAMPLE.data() + KWD_METADATA_EXAMPLE.length()); + ASSERT_TRUE(testRecognizeSucceeds( + *m_audioProvider, + Initiator::WAKEWORD, + AudioInputProcessor::INVALID_INDEX, + AudioInputProcessor::INVALID_INDEX, + KEYWORD_TEXT, + RecognizeStopPoint::NONE, + nullptr, + metadata, + "", + EXPECTED_ENCODING_FORMATS)); +} + +/// This function verifies that recognize() works for multiple audio streams when stopCapture is called after message is +/// sent +TEST_F(AudioInputProcessorTest, test_recognizeWakewordStopAfterSendForMultiStreams) { + setupEncoderTest(); + testRequestEncodingAudioFormatsSucceeds(); + + auto metadata = std::make_shared>(); + metadata->assign(KWD_METADATA_EXAMPLE.data(), KWD_METADATA_EXAMPLE.data() + KWD_METADATA_EXAMPLE.length()); + ASSERT_TRUE(testRecognizeSucceeds( + *m_audioProvider, + Initiator::WAKEWORD, + AudioInputProcessor::INVALID_INDEX, + AudioInputProcessor::INVALID_INDEX, + KEYWORD_TEXT, + RecognizeStopPoint::AFTER_SEND, + nullptr, + metadata, + "", + EXPECTED_ENCODING_FORMATS)); +} + +/// This function verifies that recognize() works for multiple audio streams when stopCapture is called immediately +/// after recognize() is called. +TEST_F(AudioInputProcessorTest, test_recognizeWakewordStopAfterRecognizeForMultiStreams) { + setupEncoderTest(); + testRequestEncodingAudioFormatsSucceeds(); + + auto metadata = std::make_shared>(); + metadata->assign(KWD_METADATA_EXAMPLE.data(), KWD_METADATA_EXAMPLE.data() + KWD_METADATA_EXAMPLE.length()); + ASSERT_TRUE(testRecognizeSucceeds( + *m_audioProvider, + Initiator::WAKEWORD, + AudioInputProcessor::INVALID_INDEX, + AudioInputProcessor::INVALID_INDEX, + KEYWORD_TEXT, + RecognizeStopPoint::AFTER_RECOGNIZE, + nullptr, + metadata, + "", + EXPECTED_ENCODING_FORMATS)); +} + +/// This function verifies that recognize() works for multiple audio streams when stopCapture is called immediately +/// after onContextAvailable() called. +TEST_F(AudioInputProcessorTest, test_recognizeWakewordStopAfterContextForMultiStreams) { + setupEncoderTest(); + testRequestEncodingAudioFormatsSucceeds(); + + auto metadata = std::make_shared>(); + metadata->assign(KWD_METADATA_EXAMPLE.data(), KWD_METADATA_EXAMPLE.data() + KWD_METADATA_EXAMPLE.length()); + ASSERT_TRUE(testRecognizeSucceeds( + *m_audioProvider, + Initiator::WAKEWORD, + AudioInputProcessor::INVALID_INDEX, + AudioInputProcessor::INVALID_INDEX, + KEYWORD_TEXT, + RecognizeStopPoint::AFTER_CONTEXT, + nullptr, + metadata, + "", + EXPECTED_ENCODING_FORMATS)); +} + +/// This function verifies that recognize() works for multiple audio streams when stopCapture() is called after +/// onFocusChanged() called. +TEST_F(AudioInputProcessorTest, test_recognizeWakewordStopCaptureAfterFocusForMultiStreams) { + setupEncoderTest(); + testRequestEncodingAudioFormatsSucceeds(); + + auto metadata = std::make_shared>(); + metadata->assign(KWD_METADATA_EXAMPLE.data(), KWD_METADATA_EXAMPLE.data() + KWD_METADATA_EXAMPLE.length()); + ASSERT_TRUE(testRecognizeSucceeds( + *m_audioProvider, + Initiator::WAKEWORD, + AudioInputProcessor::INVALID_INDEX, + AudioInputProcessor::INVALID_INDEX, + KEYWORD_TEXT, + RecognizeStopPoint::AFTER_FOCUS, + nullptr, + metadata, + "", + EXPECTED_ENCODING_FORMATS)); +} + +/** + * This function verifies that recognize() works for multiple streams when valid begin and end indices are provided. + */ +TEST_F(AudioInputProcessorTest, test_recognizeWakewordWithGoodBeginAndEndForMultiStreams) { + avsCommon::avs::AudioInputStream::Index begin = PREROLL_WORDS; + avsCommon::avs::AudioInputStream::Index end = PREROLL_WORDS + WAKEWORD_WORDS; + + setupEncoderTest(); + testRequestEncodingAudioFormatsSucceeds(); + + auto metadata = std::make_shared>(); + metadata->assign(KWD_METADATA_EXAMPLE.data(), KWD_METADATA_EXAMPLE.data() + KWD_METADATA_EXAMPLE.length()); + ASSERT_TRUE(testRecognizeSucceeds( + *m_audioProvider, + Initiator::WAKEWORD, + begin, + end, + KEYWORD_TEXT, + RecognizeStopPoint::NONE, + nullptr, + metadata, + "", + EXPECTED_ENCODING_FORMATS)); +} + +/// This function verifies that recognize() works for multiple audio streams in State::RECOGNIZING when the previous * +/// recognize used the CLOSE_TALK profile. Disabled for potential race condition in SpeechEncoder. +TEST_F(AudioInputProcessorTest, test_recognizeBargeInWhileRecognizingCloseTalkForMultiStreams) { + auto audioProvider = *m_audioProvider; + audioProvider.profile = ASRProfile::CLOSE_TALK; + setupEncoderTest(); + testRequestEncodingAudioFormatsSucceeds(); + + ASSERT_TRUE(testRecognizeSucceeds( + audioProvider, + Initiator::TAP, + AudioInputProcessor::INVALID_INDEX, + AudioInputProcessor::INVALID_INDEX, + "", + RecognizeStopPoint::NONE, + nullptr, + nullptr, + "", + EXPECTED_ENCODING_FORMATS)); + ASSERT_TRUE(testRecognizeSucceeds( + *m_audioProvider, + Initiator::TAP, + AudioInputProcessor::INVALID_INDEX, + AudioInputProcessor::INVALID_INDEX, + "", + RecognizeStopPoint::NONE, + nullptr, + nullptr, + "", + EXPECTED_ENCODING_FORMATS)); +} + +/// This function verifies that @c AudioInputProcessor::stopCapture() works in @c State::RECOGNIZING with multiple audio +/// streams. +TEST_F(AudioInputProcessorTest, test_stopCaptureWhenRecognizingForMultiStreams) { + setupEncoderTest(); + testRequestEncodingAudioFormatsSucceeds(); + + ASSERT_TRUE(testRecognizeSucceeds( + *m_audioProvider, + Initiator::TAP, + AudioInputProcessor::INVALID_INDEX, + AudioInputProcessor::INVALID_INDEX, + "", + RecognizeStopPoint::NONE, + nullptr, + nullptr, + "", + EXPECTED_ENCODING_FORMATS)); + ASSERT_TRUE(testStopCaptureSucceeds()); + + for (auto& namedReader : m_recognizeEvent->getNamedReaders()) { + auto readStatus = avsCommon::avs::attachment::AttachmentReader::ReadStatus::OK; + std::vector buf(SDS_WORDS * SDS_WORDSIZE); + EXPECT_EQ(namedReader->reader->read(buf.data(), buf.size(), &readStatus), 0U); + ASSERT_EQ(readStatus, avsCommon::avs::attachment::AttachmentReader::ReadStatus::CLOSED); + } +} + +/// This function verifies that after call requestEncodingAudioFormats with only one format in the request, AIP will +/// still send resolved MessageRequest. +TEST_F(AudioInputProcessorTest, test_recognizeWorksWithOneFormatInRequestEncodingAudioFormats) { + AudioInputProcessor::EncodingFormatRequest encodingReq = { + {"LOCAL", {avsCommon::utils::AudioFormat::Encoding::LPCM, avsCommon::utils::AudioFormat::Encoding::LPCM}}}; + AudioInputProcessor::EncodingFormatResponse expectedRsp = { + {"LOCAL", avsCommon::utils::AudioFormat::Encoding::LPCM}}; + + EXPECT_EQ(m_audioInputProcessor->requestEncodingAudioFormats(encodingReq), expectedRsp); + EXPECT_EQ(m_audioInputProcessor->getEncodingAudioFormats(), expectedRsp); + + auto begin = AudioInputProcessor::INVALID_INDEX; + auto end = AudioInputProcessor::INVALID_INDEX; + EXPECT_TRUE(testRecognizeSucceeds(*m_audioProvider, Initiator::WAKEWORD, begin, end, KEYWORD_TEXT)); +} } // namespace test } // namespace aip } // namespace capabilityAgents -} // namespace alexaClientSDK +} // namespace alexaClientSDK \ No newline at end of file diff --git a/CapabilityAgents/Alexa/CMakeLists.txt b/CapabilityAgents/Alexa/CMakeLists.txt index df5dcd2801..5b13c0e3df 100644 --- a/CapabilityAgents/Alexa/CMakeLists.txt +++ b/CapabilityAgents/Alexa/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.0) project(Alexa LANGUAGES CXX) -include(${AVS_CORE}/build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/CapabilityAgents/Alexa/include/Alexa/AlexaEventProcessedNotifier.h b/CapabilityAgents/Alexa/include/Alexa/AlexaEventProcessedNotifier.h index a707fda961..4cc3cfc09d 100644 --- a/CapabilityAgents/Alexa/include/Alexa/AlexaEventProcessedNotifier.h +++ b/CapabilityAgents/Alexa/include/Alexa/AlexaEventProcessedNotifier.h @@ -16,6 +16,9 @@ #ifndef ALEXA_CLIENT_SDK_CAPABILITYAGENTS_ALEXA_INCLUDE_ALEXA_ALEXAEVENTPROCESSEDNOTIFIER_H_ #define ALEXA_CLIENT_SDK_CAPABILITYAGENTS_ALEXA_INCLUDE_ALEXA_ALEXAEVENTPROCESSEDNOTIFIER_H_ +#include + +#include #include #include @@ -24,10 +27,18 @@ namespace capabilityAgents { namespace alexa { /** - * Implementation of AlexaEventProcessedNotifierInterface. + * Relays notifications when Alexa.EventProcessed directive is received. */ -using AlexaEventProcessedNotifier = - acsdkNotifier::Notifier; +class AlexaEventProcessedNotifier + : public acsdkNotifier::Notifier { +public: + /** + * Factory method. + * @return A new instance of @c AlexaEventProcessedNotifierInterface. + */ + static std::shared_ptr + createAlexaEventProcessedNotifierInterface(); +}; } // namespace alexa } // namespace capabilityAgents diff --git a/CapabilityAgents/Alexa/src/AlexaEventProcessedNotifier.cpp b/CapabilityAgents/Alexa/src/AlexaEventProcessedNotifier.cpp new file mode 100644 index 0000000000..fa562fc044 --- /dev/null +++ b/CapabilityAgents/Alexa/src/AlexaEventProcessedNotifier.cpp @@ -0,0 +1,29 @@ +/* + * 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 "Alexa/AlexaEventProcessedNotifier.h" + +namespace alexaClientSDK { +namespace capabilityAgents { +namespace alexa { + +std::shared_ptr +AlexaEventProcessedNotifier::createAlexaEventProcessedNotifierInterface() { + return std::make_shared(); +} + +} // namespace alexa +} // namespace capabilityAgents +} // namespace alexaClientSDK diff --git a/CapabilityAgents/Alexa/src/CMakeLists.txt b/CapabilityAgents/Alexa/src/CMakeLists.txt index 328d3ce522..96510885c1 100644 --- a/CapabilityAgents/Alexa/src/CMakeLists.txt +++ b/CapabilityAgents/Alexa/src/CMakeLists.txt @@ -2,6 +2,7 @@ add_definitions("-DACSDK_LOG_MODULE=alexa") add_library( Alexa SHARED + AlexaEventProcessedNotifier.cpp AlexaInterfaceCapabilityAgent.cpp AlexaInterfaceMessageSender.cpp ) diff --git a/CapabilityAgents/ApiGateway/CMakeLists.txt b/CapabilityAgents/ApiGateway/CMakeLists.txt index c92dd9ded8..3576543e09 100644 --- a/CapabilityAgents/ApiGateway/CMakeLists.txt +++ b/CapabilityAgents/ApiGateway/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.0) project(ApiGateway LANGUAGES CXX) -include(${AVS_CORE}/build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/CapabilityAgents/CMakeLists.txt b/CapabilityAgents/CMakeLists.txt index 3e3e2c4a65..e673ee26b7 100644 --- a/CapabilityAgents/CMakeLists.txt +++ b/CapabilityAgents/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.0) project(CapabilityAgents LANGUAGES CXX) -include(${AVS_CORE}/build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) set(CAPABILITY_AGENTS "AIP" diff --git a/CapabilityAgents/InteractionModel/CMakeLists.txt b/CapabilityAgents/InteractionModel/CMakeLists.txt index f66b0df387..de447aeb56 100644 --- a/CapabilityAgents/InteractionModel/CMakeLists.txt +++ b/CapabilityAgents/InteractionModel/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.0 FATAL_ERROR) project(InteractionModel LANGUAGES CXX) -include(${AVS_CORE}/build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") \ No newline at end of file diff --git a/CapabilityAgents/SpeakerManager/CMakeLists.txt b/CapabilityAgents/SpeakerManager/CMakeLists.txt index d6155ef290..d5a55a27b2 100644 --- a/CapabilityAgents/SpeakerManager/CMakeLists.txt +++ b/CapabilityAgents/SpeakerManager/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.0) project(SpeakerManager LANGUAGES CXX) -include(${AVS_CORE}/build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManager.h b/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManager.h index 255f4656b1..16381bc7f7 100644 --- a/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManager.h +++ b/CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManager.h @@ -357,7 +357,7 @@ class SpeakerManager /** * Function to set a limit on the maximum volume. This runs on a worker thread. * - * @param type The type of speaker to modify mute for. + * @param type The type of speaker to set a limit on the maximum volume. * @return A bool indicating success. */ bool executeSetMaximumVolumeLimit(const int8_t maximumVolumeLimit); @@ -367,7 +367,7 @@ class SpeakerManager * Function to get the speaker settings for a specific @c ChannelVolumeInterface Type. * This runs on a worker thread. * - * @param type The type of speaker to modify mute for. + * @param type The type of speaker to get speaker settings. * @param[out] settings The settings if successful. * @return A bool indicating success. */ @@ -375,6 +375,26 @@ class SpeakerManager avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings* settings); + /** + * Function to set the speaker settings for a specific @c ChannelVolumeInterface Type. + * This runs on a worker thread. + * + * @param type The type of speaker to set speaker settings. + * @param settings The new settings. + * @return A bool indicating success. + */ + bool executeSetSpeakerSettings( + const avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, + const avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings& settings); + + /** + * Function that initializes and populates @c m_speakerSettings for the given @c type's speaker settings. + * + * @param type The type of speaker to initialize + * @return A bool indicating success. + */ + bool executeInitializeSpeakerSettings(avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type); + /** * Function to send events when settings have changed. * This runs on a worker thread. @@ -402,17 +422,6 @@ class SpeakerManager const avsCommon::sdkInterfaces::ChannelVolumeInterface::Type& type, const avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings& settings); - /** - * Validates that all speakers with the given @c Type have the same @c SpeakerSettings. - * - * @param type The type of speaker to validate. - * @param[out] settings The settings that will be return if consistent. - * @return A bool indicating success. - */ - bool validateSpeakerSettingsConsistency( - avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type, - avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings* settings); - /** * Get the maximum volume limit. * @@ -470,6 +479,12 @@ class SpeakerManager /// maximumVolumeLimit The maximum volume level speakers in this system can reach. int8_t m_maximumVolumeLimit; + /// Mapping of each speaker type to its speaker settings. + std::map< + avsCommon::sdkInterfaces::ChannelVolumeInterface::Type, + avsCommon::sdkInterfaces::SpeakerInterface::SpeakerSettings> + m_speakerSettings; + /// An executor to perform operations on a worker thread. avsCommon::utils::threading::Executor m_executor; }; diff --git a/CapabilityAgents/SpeakerManager/src/SpeakerManager.cpp b/CapabilityAgents/SpeakerManager/src/SpeakerManager.cpp index 0a1c122ece..4ea58de9ef 100644 --- a/CapabilityAgents/SpeakerManager/src/SpeakerManager.cpp +++ b/CapabilityAgents/SpeakerManager/src/SpeakerManager.cpp @@ -222,14 +222,17 @@ void SpeakerManager::addChannelVolumeInterfaceIntoSpeakerMap( 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 (!executeInitializeSpeakerSettings(type)) { + ACSDK_ERROR(LX("executeInitializeSpeakerSettings failed")); + } + // If we have one AVS_SPEAKER_VOLUME speaker, update the Context initially. 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")); + if (!executeGetSpeakerSettings(type, &settings) || !updateContextManager(type, settings)) { + ACSDK_ERROR(LX(__func__).m("getSpeakerSettingsFailed or initialUpdateContextManagerFailed")); } } } else { @@ -252,11 +255,11 @@ std::shared_ptr getSpeakerCapabilityConfiguration() { } DirectiveHandlerConfiguration SpeakerManager::getConfiguration() const { - auto audioNonBlockingPolicy = BlockingPolicy(BlockingPolicy::MEDIUM_AUDIO, false); + auto neitherNonBlockingPolicy = BlockingPolicy(BlockingPolicy::MEDIUMS_NONE, false); DirectiveHandlerConfiguration configuration; - configuration[SET_VOLUME] = audioNonBlockingPolicy; - configuration[ADJUST_VOLUME] = audioNonBlockingPolicy; - configuration[SET_MUTE] = audioNonBlockingPolicy; + configuration[SET_VOLUME] = neitherNonBlockingPolicy; + configuration[ADJUST_VOLUME] = neitherNonBlockingPolicy; + configuration[SET_MUTE] = neitherNonBlockingPolicy; return configuration; } @@ -514,63 +517,6 @@ void SpeakerManager::removeSpeakerManagerObserver( }); } -bool SpeakerManager::validateSpeakerSettingsConsistency( - ChannelVolumeInterface::Type type, - SpeakerInterface::SpeakerSettings* settings) { - 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); - return false; - } - submitMetric(m_metricRecorder, "noSpeakersWithType", 0); - - // Get settings value to compare the rest against. - 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; - } - - // 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 != 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(currentSpeaker.volume)) - .d("mute", currentSpeaker.mute)); - consistent = false; - break; - } - } - - ACSDK_DEBUG9(LX("validateSpeakerSettingsConsistencyResult").d("consistent", consistent)); - submitMetric(m_metricRecorder, "speakersCannotGetSetting", 0); - - if (consistent && settings) { - submitMetric(m_metricRecorder, "speakersInconsistent", 0); - settings->volume = comparator.volume; - settings->mute = comparator.mute; - ACSDK_DEBUG9( - LX("validateSpeakerSettings").d("volume", static_cast(settings->volume)).d("mute", settings->mute)); - } - - return consistent; -} - bool SpeakerManager::updateContextManager( const ChannelVolumeInterface::Type& type, const SpeakerInterface::SpeakerSettings& settings) { @@ -662,7 +608,7 @@ bool SpeakerManager::executeSetVolume( SpeakerInterface::SpeakerSettings settings; if (!executeGetSpeakerSettings(type, &settings)) { - ACSDK_ERROR(LX("executeSetVolumeFailed").d("reason", "speakerSettingsInconsistent")); + ACSDK_ERROR(LX("executeSetVolumeFailed").d("reason", "executeGetSpeakerSettingsFailed")); return false; } const int8_t previousVolume = settings.volume; @@ -684,9 +630,9 @@ bool SpeakerManager::executeSetVolume( return true; }); - // All initialized speakers controlled by directives with the same type should have the same state. - if (!validateSpeakerSettingsConsistency(type, &settings)) { - ACSDK_ERROR(LX("executeSetVolumeFailed").d("reason", "speakerSettingsInconsistent")); + settings.volume = adjustedVolume; + if (!executeSetSpeakerSettings(type, settings)) { + ACSDK_ERROR(LX("executeSetVolumeFailed").d("reason", "executeSetSpeakerSettingsFailed")); return false; } @@ -711,9 +657,8 @@ bool SpeakerManager::executeRestoreVolume( SpeakerManagerObserverInterface::Source source) { SpeakerInterface::SpeakerSettings settings; - // All initialized speakers controlled by directives with the same type should have the same state. - if (!validateSpeakerSettingsConsistency(type, &settings)) { - ACSDK_ERROR(LX("executeAdjustVolumeFailed").d("reason", "initialSpeakerSettingsInconsistent")); + if (!executeGetSpeakerSettings(type, &settings)) { + ACSDK_ERROR(LX("executeRestoreVolumeFailed").d("reason", "getSpeakerSettingsFailed")); return false; } @@ -767,37 +712,49 @@ bool SpeakerManager::executeAdjustVolume( submitMetric(m_metricRecorder, "adjustVolume", 1); SpeakerInterface::SpeakerSettings settings; if (!executeGetSpeakerSettings(type, &settings)) { - ACSDK_ERROR(LX("executeAdjustVolumeFailed").d("reason", "speakerSettingsInconsistent")); + ACSDK_ERROR(LX("executeAdjustVolumeFailed").d("reason", "executeGetSpeakerSettingsFailed")); return false; } - - auto maxVolumeLimit = getMaximumVolumeLimit(); - // if the current volume settings is higher than the maxVolumelimit, reset it to maxVolumeLimit to apply delta to. - if (settings.volume > maxVolumeLimit) { - ACSDK_DEBUG0(LX("adjustingSettingsVolumeValue") - .d("reason", "valueHigherThanLimit") - .d("value", (int)settings.volume) - .d("maximumVolumeLimitSetting", (int)maxVolumeLimit)); - settings.volume = maxVolumeLimit; - } - const int8_t previousVolume = settings.volume; - // recalculate the delta if needed. - if (delta > 0) { - delta = std::min(static_cast(settings.volume + delta), static_cast(maxVolumeLimit)) - settings.volume; - } else { - delta = - std::max(static_cast(settings.volume + delta), static_cast(AVS_SET_VOLUME_MIN)) - settings.volume; - } - + auto maxVolumeLimit = getMaximumVolumeLimit(); auto begin = it->second.begin(); auto end = it->second.end(); - retryAndApplySettings([&begin, end, delta] { + + retryAndApplySettings([&begin, end, delta, maxVolumeLimit] { // Go through list of Speakers with ChannelVolumeInterface::Type equal // to type, and call adjustUnduckedVolume. + SpeakerInterface::SpeakerSettings speakerSettings; while (begin != end) { - if (!(*begin)->adjustUnduckedVolume(delta)) { + if (!(*begin)->getSpeakerSettings(&speakerSettings)) { + return false; + } + // if the current volume settings is higher than the maxVolumelimit, reset it to maxVolumeLimit to apply + // delta to. + if (speakerSettings.volume > maxVolumeLimit) { + ACSDK_DEBUG0(LX("adjustingSettingsVolumeValue") + .d("reason", "valueHigherThanLimit") + .d("value", (int)speakerSettings.volume) + .d("maximumVolumeLimitSetting", (int)maxVolumeLimit)); + speakerSettings.volume = maxVolumeLimit; + } + // recalculate the delta if needed. + int8_t newDelta = delta; + if (delta > 0) { + newDelta = static_cast( + std::min( + static_cast(speakerSettings.volume) + static_cast(delta), + static_cast(maxVolumeLimit)) - + speakerSettings.volume); + } else { + newDelta = static_cast( + std::max( + static_cast(speakerSettings.volume) + static_cast(delta), + static_cast(AVS_SET_VOLUME_MIN)) - + speakerSettings.volume); + } + + if (!(*begin)->adjustUnduckedVolume(newDelta)) { return false; } begin++; @@ -805,8 +762,15 @@ bool SpeakerManager::executeAdjustVolume( return true; }); - if (!validateSpeakerSettingsConsistency(type, &settings)) { - ACSDK_ERROR(LX("executeAdjustVolumeFailed").d("reason", "speakerSettingsInconsistent")); + if (delta > 0) { + settings.volume = static_cast( + std::min(static_cast(settings.volume) + static_cast(delta), static_cast(maxVolumeLimit))); + } else { + settings.volume = static_cast(std::max( + static_cast(settings.volume) + static_cast(delta), static_cast(AVS_SET_VOLUME_MIN))); + } + if (!executeSetSpeakerSettings(type, settings)) { + ACSDK_ERROR(LX("executeAdjustVolumeFailed").d("reason", "executeSetSpeakerSettingsFailed")); return false; } @@ -857,18 +821,16 @@ bool SpeakerManager::executeSetMute( bool mute, const SpeakerManagerInterface::NotificationProperties& properties) { ACSDK_DEBUG9(LX("executeSetMuteCalled").d("mute", mute)); + SpeakerInterface::SpeakerSettings settings; + if (!executeGetSpeakerSettings(type, &settings)) { + ACSDK_ERROR(LX("executeSetMuteFailed").d("reason", "executeGetSpeakerSettingsFailed")); + return false; + } // if unmuting an already unmute speaker, then ignore the request - if (!mute) { - SpeakerInterface::SpeakerSettings settings; - // All initialized speakers controlled by directives with the same type should have the same state. - if (!validateSpeakerSettingsConsistency(type, &settings)) { - ACSDK_WARN(LX("executeSetMuteWarn") - .m("cannot check if device is muted") - .d("reason", "speakerSettingsInconsistent")); - } else if (!settings.mute) { - return true; - } + if (!mute && !settings.mute) { + ACSDK_DEBUG5(LX("executeSetMute").m("Device is already unmuted")); + return true; } auto it = m_speakerMap.find(type); @@ -891,11 +853,9 @@ bool SpeakerManager::executeSetMute( }); submitMetric(m_metricRecorder, mute ? "setMute" : "setUnMute", 1); - SpeakerInterface::SpeakerSettings settings; - - // All initialized speakers controlled by directives with the same type should have the same state. - if (!validateSpeakerSettingsConsistency(type, &settings)) { - ACSDK_ERROR(LX("executeSetMute").d("reason", "speakerSettingsInconsistent")); + settings.mute = mute; + if (!executeSetSpeakerSettings(type, settings)) { + ACSDK_ERROR(LX("executeSetMuteFailed").d("reason", "executeSetSpeakerSettingsFailed")); return false; } @@ -991,16 +951,55 @@ bool SpeakerManager::executeGetSpeakerSettings( ChannelVolumeInterface::Type type, SpeakerInterface::SpeakerSettings* settings) { ACSDK_DEBUG9(LX("executeGetSpeakerSettingsCalled")); + if (!settings) { + ACSDK_ERROR(LX("executeGetSpeakerSettingsFailed").d("reason", "nullSettings")); + return false; + } if (m_speakerMap.find(type) == m_speakerMap.end()) { ACSDK_ERROR(LX("executeGetSpeakerSettingsFailed").d("reason", "noSpeakersWithType").d("type", type)); return false; } - // All initialized speakers controlled by directives with the same type should have the same state. - if (!validateSpeakerSettingsConsistency(type, settings)) { - ACSDK_ERROR(LX("executeGetSpeakerSettingsCalled").d("reason", "speakerSettingsInconsistent")); + // m_speakerSettings is the main source of truth, only query actual speaker as a fallback + if (m_speakerSettings.find(type) == m_speakerSettings.end()) { + ACSDK_WARN(LX("executeGetSpeakerSettings").m("noSpeakerSettingsWithType, initializing it").d("type", type)); + if (!executeInitializeSpeakerSettings(type)) { + ACSDK_ERROR(LX("executeGetSpeakerSettingsFailed").d("reason", "initializeSpeakerSettingsFailed")); + return false; + } + } + *settings = m_speakerSettings[type]; + return true; +} + +bool SpeakerManager::executeInitializeSpeakerSettings(avsCommon::sdkInterfaces::ChannelVolumeInterface::Type type) { + ACSDK_DEBUG5(LX("executeInitializeSpeakerSettings").d("type", type)); + auto it = m_speakerMap.find(type); + if (m_speakerMap.end() == it) { + ACSDK_ERROR(LX("executeInitializeSpeakerSettings").d("reason", "noSpeakersWithTypeFound").d("type", type)); + return false; + } + + auto begin = it->second.begin(); + SpeakerInterface::SpeakerSettings settings; + if (!(*begin)->getSpeakerSettings(&settings)) { + ACSDK_ERROR(LX("executeInitializeSpeakerSettings").d("reason", "gettingSpeakerSettingsFailed").d("type", type)); + return false; + } + + m_speakerSettings[type] = settings; + return true; +} + +bool SpeakerManager::executeSetSpeakerSettings( + const ChannelVolumeInterface::Type type, + const SpeakerInterface::SpeakerSettings& settings) { + ACSDK_DEBUG9(LX("executeSetSpeakerSettingsCalled")); + if (m_speakerMap.end() == m_speakerMap.find(type)) { + ACSDK_ERROR(LX("executeSetSpeakerSettings").d("reason", "noSpeakersWithTypeFound").d("type", type)); return false; } + m_speakerSettings[type] = settings; return true; } diff --git a/CapabilityAgents/SpeakerManager/test/SpeakerManagerTest.cpp b/CapabilityAgents/SpeakerManager/test/SpeakerManagerTest.cpp index 9137e1b2e4..58b9a296d5 100644 --- a/CapabilityAgents/SpeakerManager/test/SpeakerManagerTest.cpp +++ b/CapabilityAgents/SpeakerManager/test/SpeakerManagerTest.cpp @@ -90,6 +90,9 @@ static const int8_t VALID_MAXIMUM_VOLUME_LIMIT = AVS_SET_VOLUME_MAX - 10; static const int8_t INVALID_MAXIMUM_VOLUME_LIMIT = AVS_SET_VOLUME_MAX + 10; #endif +/// A valid delta to adjust the volume. +static const int8_t VALID_VOLUME_ADJUSTMENT = 10; + /** * A mock object to test that the observer is being correctly notified. */ @@ -386,60 +389,38 @@ TEST_F(SpeakerManagerTest, test_adjustVolumeOverBounds) { } /* - * Test setVolume when the channelvolume interfaces are out of sync. The operation should fail. + * Test if one speaker is out of sync, getSpeakingSettings should return the cached value correctly. */ -TEST_F(SpeakerManagerTest, test_setVolumeOutOfSync) { - auto channelVolumeInterface = std::make_shared>(); - channelVolumeInterface->DelegateToReal(); - +TEST_F(SpeakerManagerTest, test_getCachedSettings) { + // Prepare two speakers with the same type AVS_SPEAKER_VOLUME + auto channelVolumeInterface1 = std::make_shared>(); auto channelVolumeInterface2 = std::make_shared>(); - EXPECT_CALL(*channelVolumeInterface2, getSpeakerType()) - .WillRepeatedly(Return(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)); - EXPECT_CALL(*channelVolumeInterface2, setUnduckedVolume(_)).WillRepeatedly(Return(true)); - // Set speaker to be out of sync. - EXPECT_CALL(*channelVolumeInterface2, getSpeakerSettings(_)).WillRepeatedly(Return(false)); + channelVolumeInterface1->DelegateToReal(); + channelVolumeInterface2->DelegateToReal(); + // Get speaker settings from the first speaker of each type during initialization. + EXPECT_CALL(*channelVolumeInterface1, getSpeakerSettings(_)).Times(Exactly(1)); m_speakerManager = SpeakerManager::create( - {channelVolumeInterface, channelVolumeInterface2}, + {channelVolumeInterface1, channelVolumeInterface2}, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); - m_speakerManager->addSpeakerManagerObserver(m_observer); - SpeakerManagerInterface::NotificationProperties properties; + // If a speaker changes its volume and is out of sync with the rest speakers of the same type, querying speaker + // settings from SpeakerManager should return the cached volume correctly. + channelVolumeInterface2->setUnduckedVolume(AVS_SET_VOLUME_MAX); + channelVolumeInterface2->setMute(MUTE); + SpeakerInterface::SpeakerSettings settings; std::future future = - m_speakerManager->setVolume(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_SET_VOLUME_MAX, properties); - ASSERT_FALSE(future.get()); -} - -/* - * Test adjustVolume when the speaker interfaces are out of sync. The operation should fail. - */ -TEST_F(SpeakerManagerTest, test_adjustVolumeOutOfSync) { - auto channelVolumeInterface = std::make_shared>(); - channelVolumeInterface->DelegateToReal(); - - auto channelVolumeInterface2 = std::make_shared>(); - EXPECT_CALL(*channelVolumeInterface2, getSpeakerType()) - .WillRepeatedly(Return(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)); - EXPECT_CALL(*channelVolumeInterface2, setUnduckedVolume(_)).WillRepeatedly(Return(true)); - // Set speaker to be out of sync. - EXPECT_CALL(*channelVolumeInterface2, getSpeakerSettings(_)).WillRepeatedly(Return(false)); + m_speakerManager->getSpeakerSettings(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, &settings); + ASSERT_TRUE(future.get()); + ASSERT_EQ(settings.volume, DEFAULT_SETTINGS.volume); + ASSERT_EQ(settings.mute, DEFAULT_SETTINGS.mute); - m_speakerManager = SpeakerManager::create( - {channelVolumeInterface, channelVolumeInterface2}, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); - m_speakerManager->addSpeakerManagerObserver(m_observer); - SpeakerManagerInterface::NotificationProperties properties; - std::future future = m_speakerManager->adjustVolume( - ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, AVS_ADJUST_VOLUME_MAX, properties); - ASSERT_FALSE(future.get()); + ASSERT_TRUE(channelVolumeInterface2->getSpeakerSettings(&settings)); + ASSERT_EQ(settings.volume, AVS_SET_VOLUME_MAX); + ASSERT_EQ(settings.mute, MUTE); } /* @@ -515,64 +496,6 @@ TEST_F(SpeakerManagerTest, test_eventNotSentWhenSetVolumeUnchanged) { } } -/* - * Test setMute when the speaker interfaces are out of sync. The operation should fail. - */ -TEST_F(SpeakerManagerTest, test_setMuteOutOfSync) { - auto channelVolumeInterface = std::make_shared>(); - channelVolumeInterface->DelegateToReal(); - - auto channelVolumeInterface2 = std::make_shared>(); - EXPECT_CALL(*channelVolumeInterface2, getSpeakerType()) - .WillRepeatedly(Return(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)); - EXPECT_CALL(*channelVolumeInterface2, setMute(_)).WillRepeatedly(Return(true)); - // Set speaker to be out of sync. - EXPECT_CALL(*channelVolumeInterface2, getSpeakerSettings(_)).WillRepeatedly(Return(false)); - m_speakerManager = SpeakerManager::create( - {channelVolumeInterface, channelVolumeInterface2}, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); - m_speakerManager->addSpeakerManagerObserver(m_observer); - - SpeakerManagerInterface::NotificationProperties properties; - std::future future = - m_speakerManager->setMute(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, MUTE, properties); - ASSERT_FALSE(future.get()); -} - -/** - * Test getSpeakerSettings when speakers are out of sync. The operation should fail. - */ -TEST_F(SpeakerManagerTest, test_getSpeakerSettingsSpeakersOutOfSync) { - auto channelVolumeInterface = std::make_shared>(); - channelVolumeInterface->DelegateToReal(); - - auto channelVolumeInterface2 = std::make_shared>(); - EXPECT_CALL(*channelVolumeInterface2, getSpeakerType()) - .WillRepeatedly(Return(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME)); - // Set speaker to be out of sync. - EXPECT_CALL(*channelVolumeInterface2, getSpeakerSettings(_)).WillRepeatedly(Return(false)); - - m_speakerManager = SpeakerManager::create( - {channelVolumeInterface, channelVolumeInterface2}, - m_mockContextManager, - m_mockMessageSender, - m_mockExceptionSender, - m_metricRecorder); - - EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); - m_speakerManager->addSpeakerManagerObserver(m_observer); - - SpeakerInterface::SpeakerSettings settings; - std::future future = - m_speakerManager->getSpeakerSettings(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, &settings); - ASSERT_FALSE(future.get()); -} - /** * Test getConfiguration and ensure that all directives are handled. */ @@ -583,10 +506,10 @@ TEST_F(SpeakerManagerTest, test_getConfiguration) { channelVolumeInterfaceVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); auto configuration = m_speakerManager->getConfiguration(); - auto audioNonBlockingPolicy = BlockingPolicy(BlockingPolicy::MEDIUM_AUDIO, false); - ASSERT_EQ(configuration[SET_VOLUME], audioNonBlockingPolicy); - ASSERT_EQ(configuration[ADJUST_VOLUME], audioNonBlockingPolicy); - ASSERT_EQ(configuration[SET_MUTE], audioNonBlockingPolicy); + auto neitherNonBlockingPolicy = BlockingPolicy(BlockingPolicy::MEDIUMS_NONE, false); + ASSERT_EQ(configuration[SET_VOLUME], neitherNonBlockingPolicy); + ASSERT_EQ(configuration[ADJUST_VOLUME], neitherNonBlockingPolicy); + ASSERT_EQ(configuration[SET_MUTE], neitherNonBlockingPolicy); } /** @@ -685,20 +608,47 @@ TEST_F(SpeakerManagerTest, test_retryAndApplySettingsForSetVolume) { } /* - * Test retryLogic for AdjustVolume on speaker type AVS_SPEAKER_VOLUME. Returning false once for speaker->setVolume() - * triggers retry and when successful returns the future of value true. + * Test retryLogic for AdjustVolume on speakers of type AVS_SPEAKER_VOLUME. Return false once for the second speaker + * during adjustVolume() to trigger a retry. The delta should not be applied again to the first speaker during retry. */ TEST_F(SpeakerManagerTest, test_retryAndApplySettingsForAdjustVolume) { - auto channelVolumeInterfaceVec = createChannelVolumeInterfaces(); + auto channelVolumeInterface1 = std::make_shared>(); + auto channelVolumeInterface2 = std::make_shared>(); + channelVolumeInterface1->DelegateToReal(); + channelVolumeInterface2->DelegateToReal(); m_speakerManager = SpeakerManager::create( - channelVolumeInterfaceVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); + {channelVolumeInterface1, channelVolumeInterface2}, + m_mockContextManager, + m_mockMessageSender, + m_mockExceptionSender, + m_metricRecorder); + auto retryTimes = 0; + EXPECT_CALL(*channelVolumeInterface2, setUnduckedVolume(_)).WillRepeatedly(InvokeWithoutArgs([&retryTimes] { + if (retryTimes++ < 1) { + return false; + } + return true; + })); + + // Expect volumeChanged event. + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).Times(Exactly(1)); std::future future = m_speakerManager->adjustVolume( ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, - AVS_SET_VOLUME_MIN, + VALID_VOLUME_ADJUSTMENT, SpeakerManagerInterface::NotificationProperties()); ASSERT_TRUE(future.get()); + + SpeakerInterface::SpeakerSettings settings1; + ASSERT_TRUE(channelVolumeInterface1->getSpeakerSettings(&settings1)); + ASSERT_EQ(settings1.volume, DEFAULT_SETTINGS.volume + VALID_VOLUME_ADJUSTMENT); + + SpeakerInterface::SpeakerSettings speakerSettings; + std::future settingsFuture = + m_speakerManager->getSpeakerSettings(ChannelVolumeInterface::Type::AVS_SPEAKER_VOLUME, &speakerSettings); + ASSERT_TRUE(settingsFuture.get()); + ASSERT_EQ(speakerSettings.volume, DEFAULT_SETTINGS.volume + VALID_VOLUME_ADJUSTMENT); } /* @@ -1011,23 +961,38 @@ TEST_P(SpeakerManagerTest, test_setMute) { */ TEST_P(SpeakerManagerTest, test_getSpeakerSettings) { std::vector> groupVec; + std::set uniqueTypes; for (auto& typeOfSpeaker : GetParam()) { auto group = std::make_shared>(typeOfSpeaker); group->DelegateToReal(); + + // There should be one call to getSpeakerSettings for the first speaker of each type. + if (uniqueTypes.find(typeOfSpeaker) == uniqueTypes.end()) { + EXPECT_CALL(*group, getSpeakerSettings(_)).Times(AtLeast(1)); + uniqueTypes.insert(typeOfSpeaker); + } + groupVec.push_back(group); } - auto uniqueTypes = getUniqueTypes(groupVec); - m_speakerManager = SpeakerManager::create( groupVec, m_mockContextManager, m_mockMessageSender, m_mockExceptionSender, m_metricRecorder); EXPECT_CALL(*m_observer, onSpeakerSettingsChanged(_, _, _)).Times(Exactly(0)); m_speakerManager->addSpeakerManagerObserver(m_observer); + for (auto speaker : groupVec) { + // SpeakerManager attempts to cache speaker settings initially. No getSpeakerSettings() call should be made to + // each speaker. + auto mockSpeaker = std::dynamic_pointer_cast>(speaker); + ASSERT_TRUE(mockSpeaker); + EXPECT_CALL(*mockSpeaker, getSpeakerSettings(_)).Times(0); + } + for (auto type : uniqueTypes) { SpeakerInterface::SpeakerSettings settings; + // Query SpeakerMananger for speaker settings, value should be cached and not queried from each speaker. std::future future = m_speakerManager->getSpeakerSettings(type, &settings); ASSERT_TRUE(future.get()); ASSERT_EQ(settings.volume, DEFAULT_SETTINGS.volume); diff --git a/CapabilityAgents/SpeechSynthesizer/CMakeLists.txt b/CapabilityAgents/SpeechSynthesizer/CMakeLists.txt index 78c94f0c8b..71de4e78a6 100644 --- a/CapabilityAgents/SpeechSynthesizer/CMakeLists.txt +++ b/CapabilityAgents/SpeechSynthesizer/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.0) project(SpeechSynthesizer LANGUAGES CXX) -include(${AVS_CORE}/build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/CapabilityAgents/System/include/System/SystemCapabilityProvider.h b/CapabilityAgents/System/include/System/SystemCapabilityProvider.h index 08d3965648..65389cc0b2 100644 --- a/CapabilityAgents/System/include/System/SystemCapabilityProvider.h +++ b/CapabilityAgents/System/include/System/SystemCapabilityProvider.h @@ -18,8 +18,10 @@ #include +#include #include #include +#include namespace alexaClientSDK { namespace capabilityAgents { @@ -29,32 +31,62 @@ namespace system { * This class handles providing configuration for the System Capability agent, since a single class * does not handle all of the capability agent's functionings. */ -class SystemCapabilityProvider : public avsCommon::sdkInterfaces::CapabilityConfigurationInterface { +class SystemCapabilityProvider + : public avsCommon::sdkInterfaces::CapabilityConfigurationInterface + , public avsCommon::sdkInterfaces::LocaleAssetsObserverInterface + , public std::enable_shared_from_this { public: /** * Create an instance of @c SystemCapabilityProvider. * * @param localeAssetsManager The locale assets manager that provides supported locales. + * @param capabilityChangeNotifier The object with which to notify observers of @c SystemCapabilityProvider + * capability configurations change. */ static std::shared_ptr create( - const std::shared_ptr& localeAssetsManager); + const std::shared_ptr& localeAssetsManager, + const std::shared_ptr& capabilityChangeNotifier); /// @name CapabilityConfigurationInterface Functions /// @{ std::unordered_set> getCapabilityConfigurations() override; /// @} + /// @name LocaleAssetsObserverInterface Functions + /// @{ + void onLocaleAssetsChanged() override; + /// @} + private: /** * Constructor. * * @param localeAssetsManager The locale assets manager that provides supported locales. + * @param capabilityChangeNotifier The object with which to notify observers of @c SystemCapabilityProvider + * capability configurations change. */ SystemCapabilityProvider( - const std::shared_ptr& localeAssetsManager); + const std::shared_ptr& localeAssetsManager, + const std::shared_ptr& capabilityChangeNotifier); + + /** + * Initialize the system capability provider. + * + * @return true on success, false on failure. + */ + bool initialize(); /// Set of capability configurations that will get published using the Capabilities API std::unordered_set> m_capabilityConfigurations; + + /// The locale assets manager. + std::shared_ptr m_assetsManager; + + /// The object to notify of @c SystemCapabilityProvider capability configurations change. + std::shared_ptr m_capabilityChangeNotifier; + + /// Mutex to serialize access to m_capabilityConfigurations. + std::mutex m_mutex; }; } // namespace system diff --git a/CapabilityAgents/System/src/CMakeLists.txt b/CapabilityAgents/System/src/CMakeLists.txt index 2574fd8c19..99e22c74ab 100644 --- a/CapabilityAgents/System/src/CMakeLists.txt +++ b/CapabilityAgents/System/src/CMakeLists.txt @@ -22,7 +22,14 @@ target_include_directories(AVSSystem PUBLIC "${RegistrationManager_SOURCE_DIR}/include" "${DeviceSettings_SOURCE_DIR}/include") -target_link_libraries(AVSSystem AVSCommon ACL CertifiedSender SQLiteStorage RegistrationManager DeviceSettings) +target_link_libraries(AVSSystem + AVSCommon + ACL + CertifiedSender + SQLiteStorage + RegistrationManager + DeviceSettings + acsdkNotifier) # install target asdk_install() diff --git a/CapabilityAgents/System/src/SystemCapabilityProvider.cpp b/CapabilityAgents/System/src/SystemCapabilityProvider.cpp index 7722fcae06..9748c8a857 100644 --- a/CapabilityAgents/System/src/SystemCapabilityProvider.cpp +++ b/CapabilityAgents/System/src/SystemCapabilityProvider.cpp @@ -62,18 +62,32 @@ static std::shared_ptr getSystemCapabil const std::shared_ptr& localeAssetsManager); std::shared_ptr SystemCapabilityProvider::create( - const std::shared_ptr& localeAssetsManager) { + const std::shared_ptr& localeAssetsManager, + const std::shared_ptr& capabilityChangeNotifier) { if (!localeAssetsManager) { ACSDK_ERROR(LX("createFailed").d("reason", "nullLocaleAssetsManager")); return nullptr; + } else if (!capabilityChangeNotifier) { + ACSDK_ERROR(LX("createFailed").d("reason", "nullCapabilityChangeNotifier")); + return nullptr; } + auto systemCapabilityProvider = std::shared_ptr( + new SystemCapabilityProvider(localeAssetsManager, capabilityChangeNotifier)); - return std::shared_ptr(new SystemCapabilityProvider(localeAssetsManager)); + if (!systemCapabilityProvider->initialize()) { + ACSDK_ERROR(LX("createFailed").d("reason", "initializationFailed")); + return nullptr; + } + return systemCapabilityProvider; } SystemCapabilityProvider::SystemCapabilityProvider( - const std::shared_ptr& localeAssetsManager) { - m_capabilityConfigurations.insert(getSystemCapabilityConfiguration(localeAssetsManager)); + const std::shared_ptr& localeAssetsManager, + const std::shared_ptr& capabilityChangeNotifier) : + m_assetsManager{localeAssetsManager}, + m_capabilityChangeNotifier{capabilityChangeNotifier} { + auto capabilityConfiguration = getSystemCapabilityConfiguration(localeAssetsManager); + m_capabilityConfigurations.insert(capabilityConfiguration); } std::shared_ptr getSystemCapabilityConfiguration( @@ -89,14 +103,36 @@ std::shared_ptr getSystemCapabilityConf LOCALE_COMBINATION_CONFIGURATION_KEY, localeAssetsManager->getSupportedLocaleCombinations()); configMap.insert({CAPABILITY_INTERFACE_CONFIGURATIONS_KEY, generator.toString()}); + ACSDK_DEBUG5(LX(__func__).d("locales", generator.toString())); + return std::make_shared(configMap); } std::unordered_set> SystemCapabilityProvider:: getCapabilityConfigurations() { + std::lock_guard lock(m_mutex); return m_capabilityConfigurations; } +void SystemCapabilityProvider::onLocaleAssetsChanged() { + ACSDK_DEBUG(LX(__func__)); + std::unique_lock lock(m_mutex); + m_capabilityConfigurations.clear(); + auto newLocales = getSystemCapabilityConfiguration(m_assetsManager); + m_capabilityConfigurations.insert(newLocales); + lock.unlock(); + m_capabilityChangeNotifier->notifyObservers( + [newLocales]( + std::shared_ptr observer) { + observer->onConfigurationChanged(*newLocales); + }); +} + +bool SystemCapabilityProvider::initialize() { + m_assetsManager->addLocaleAssetsObserver(shared_from_this()); + return true; +} + } // namespace system } // namespace capabilityAgents } // namespace alexaClientSDK diff --git a/CapabilityAgents/System/test/SystemCapabilityProviderTest.cpp b/CapabilityAgents/System/test/SystemCapabilityProviderTest.cpp new file mode 100644 index 0000000000..296d99e812 --- /dev/null +++ b/CapabilityAgents/System/test/SystemCapabilityProviderTest.cpp @@ -0,0 +1,171 @@ +/* + * 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 +#include +#include + +#include "System/SystemCapabilityProvider.h" + +using namespace alexaClientSDK::avsCommon::avs; +using namespace alexaClientSDK::avsCommon::sdkInterfaces; +using namespace alexaClientSDK::avsCommon::sdkInterfaces::test; +using namespace alexaClientSDK::avsCommon::utils::json; +using namespace testing; + +namespace alexaClientSDK { +namespace capabilityAgents { +namespace system { +namespace test { + +/// A list of test supported locales. +static const std::set SUPPORTED_LOCALES = {"en-US", "en-GB"}; + +/// A list of test supported locale combinations. +static const std::set> SUPPORTED_LOCALE_COMBINATIONS = {{"en-US", "es-US"}}; + +/// Capability configuration key used to give more details about the device configuration. +static const std::string CAPABILITY_INTERFACE_CONFIGURATIONS_KEY = "configurations"; + +/// Locale key +static const std::string LOCALES_CONFIGURATION_KEY = "locales"; + +/// Locale Combinations Key +static const std::string LOCALE_COMBINATION_CONFIGURATION_KEY = "localeCombinations"; + +/// Mock class that implements the @c CapabilityConfigurationChangeObserverInterface. +class MockCapabilityConfigurationChangeObserver + : public avsCommon::sdkInterfaces::CapabilityConfigurationChangeObserverInterface { +public: + MOCK_METHOD1(onConfigurationChanged, void(const avsCommon::avs::CapabilityConfiguration& configuration)); +}; + +/// Test harness for @c SystemCapabilityProvider +class SystemCapabilityProviderTest : public ::testing::Test { +public: + /// Set up the test harness for running a test. + void SetUp() override; + /// Clean up the test harness after running a test. + void TearDown() override; + +protected: + /// The mock @c CapabilityconfigurationChangeObserverInterface + std::shared_ptr m_mockCapabilityConfigurationChangeObserver; + + /// The @c CapabilityChangeNotifierInterface + std::shared_ptr m_capabilityChangeNotifier; + + /// A mock instance of @c LocaleAssetsManagerInterface + std::shared_ptr m_mockAssetsManager; + + /// The @c SystemCapabilityProvider to test. + std::shared_ptr m_systemCapabilityProvider; +}; + +void SystemCapabilityProviderTest::SetUp() { + m_mockCapabilityConfigurationChangeObserver = + std::make_shared>(); + m_capabilityChangeNotifier = std::make_shared(); + m_capabilityChangeNotifier->addObserver(m_mockCapabilityConfigurationChangeObserver); + + m_mockAssetsManager = std::make_shared>(); + ON_CALL(*m_mockAssetsManager, getSupportedLocales()) + .WillByDefault( + InvokeWithoutArgs([]() -> std::set { return SUPPORTED_LOCALES; })); + + m_systemCapabilityProvider = SystemCapabilityProvider::create(m_mockAssetsManager, m_capabilityChangeNotifier); + ASSERT_NE(m_systemCapabilityProvider, nullptr); +} + +void SystemCapabilityProviderTest::TearDown() { + m_capabilityChangeNotifier->removeObserver(m_mockCapabilityConfigurationChangeObserver); +} + +/// Function to verify that @c SystemCapabilityProvider::create) errors out with invalid inputs. +TEST_F(SystemCapabilityProviderTest, test_createWithInvalidInputs) { + /// With an invalid @c LocaleAssetsManagerInterface + m_systemCapabilityProvider = SystemCapabilityProvider::create(nullptr, m_capabilityChangeNotifier); + EXPECT_EQ(m_systemCapabilityProvider, nullptr); + + /// With an invalid @c CapabilityChangeNotifierInterface + m_systemCapabilityProvider = SystemCapabilityProvider::create(m_mockAssetsManager, nullptr); + EXPECT_EQ(m_systemCapabilityProvider, nullptr); +} + +/// Function to verify that @c SystemCapabilityProvider notifies observers when locale asset is changed and updates +/// its own capability configurations. +TEST_F(SystemCapabilityProviderTest, test_localeAssetsChanged) { + std::string localeString; + for (auto locale : SUPPORTED_LOCALES) { + if (!localeString.empty()) { + localeString += ","; + } + localeString += "\"" + locale + "\""; + } + + std::string localeCombinationsString; + for (auto localeCombo : SUPPORTED_LOCALE_COMBINATIONS) { + if (!localeCombinationsString.empty()) { + localeCombinationsString += ","; + } + std::string localeComboString; + for (auto locale : localeCombo) { + if (!localeComboString.empty()) { + localeComboString += ","; + } + localeComboString += "\"" + locale + "\""; + } + localeCombinationsString += localeComboString; + } + + auto oldLocalesRegex = R"(.*\[)" + localeString + R"(\].*)"; + auto newLocalesRegex = R"(.*\[)" + localeString + R"(\].*)" + R"(.*\[)" + localeCombinationsString + R"(\].*)"; + + // Check the old System capability configuration. + std::unordered_set> caps = + m_systemCapabilityProvider->getCapabilityConfigurations(); + auto cap = *caps.begin(); + auto configuration = cap->additionalConfigurations.find(CAPABILITY_INTERFACE_CONFIGURATIONS_KEY); + ASSERT_NE(configuration, cap->additionalConfigurations.end()); + EXPECT_THAT(configuration->second, MatchesRegex(oldLocalesRegex)); + + // Update the System capability configuration. + EXPECT_CALL(*m_mockAssetsManager, getSupportedLocaleCombinations()) + .WillOnce(InvokeWithoutArgs( + []() -> LocaleAssetsManagerInterface::LocaleCombinations { return SUPPORTED_LOCALE_COMBINATIONS; })); + EXPECT_CALL(*m_mockCapabilityConfigurationChangeObserver, onConfigurationChanged(_)) + .WillOnce(Invoke([this, + newLocalesRegex](const avsCommon::avs::CapabilityConfiguration& capabilityConfiguration) { + auto cap = capabilityConfiguration.additionalConfigurations.find(CAPABILITY_INTERFACE_CONFIGURATIONS_KEY); + EXPECT_THAT(cap->second, MatchesRegex(newLocalesRegex)); + + // Retrieve the new System capability configuration. + auto newCaps = m_systemCapabilityProvider->getCapabilityConfigurations(); + auto newCap = *newCaps.begin(); + auto newConfiguration = newCap->additionalConfigurations.find(CAPABILITY_INTERFACE_CONFIGURATIONS_KEY); + ASSERT_NE(newConfiguration, newCap->additionalConfigurations.end()); + EXPECT_THAT(newConfiguration->second, MatchesRegex(newLocalesRegex)); + })); + m_systemCapabilityProvider->onLocaleAssetsChanged(); +} + +} // namespace test +} // namespace system +} // namespace capabilityAgents +} // namespace alexaClientSDK \ No newline at end of file diff --git a/Captions/CMakeLists.txt b/Captions/CMakeLists.txt index eea3baa03a..5c6bd6c6bb 100644 --- a/Captions/CMakeLists.txt +++ b/Captions/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(Captions LANGUAGES CXX) -include(../build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("Component") add_subdirectory("Interface") diff --git a/CertifiedSender/CMakeLists.txt b/CertifiedSender/CMakeLists.txt index 036d529ef6..cf28c5742f 100644 --- a/CertifiedSender/CMakeLists.txt +++ b/CertifiedSender/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(CertifiedSender LANGUAGES CXX) -include(../build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/CertifiedSender/include/CertifiedSender/SQLiteMessageStorage.h b/CertifiedSender/include/CertifiedSender/SQLiteMessageStorage.h index 1a65e302b8..9cbf370126 100644 --- a/CertifiedSender/include/CertifiedSender/SQLiteMessageStorage.h +++ b/CertifiedSender/include/CertifiedSender/SQLiteMessageStorage.h @@ -77,6 +77,40 @@ class SQLiteMessageStorage : public MessageStorageInterface { private: /// The underlying database class. alexaClientSDK::storage::sqliteStorage::SQLiteDatabase m_database; + + /** + * Utility that checks if database is legacy. + * + * @return + * 1 if the database is legacy + * 0 if the database is not legacy + * -1 if there is an error while checking the database is legacy + */ + int isDatabaseLegacy(); + + /** + * Utility that drops the current database. + * WARNING: Action cannot be undone. + * + * @return true if success, otherwise false. + */ + bool dropTable(); + + /** + * Utility that deletes all message that older than 5 mins + * WARNING: Action cannot be undone + * + * @return true if success, otherwise false. + */ + bool eraseMessageOverAgeLimit(); + + /** + * Utility that keeps the size of the messages at most 25 + * WARNING: Action cannot be undone + * + * @return true if success, otherwise false. + */ + bool eraseMessageOverSizeLimit(); }; } // namespace certifiedSender diff --git a/CertifiedSender/src/CertifiedSender.cpp b/CertifiedSender/src/CertifiedSender.cpp index 3ec53b7282..45fcb4614d 100644 --- a/CertifiedSender/src/CertifiedSender.cpp +++ b/CertifiedSender/src/CertifiedSender.cpp @@ -21,6 +21,8 @@ #include #include +#include + namespace alexaClientSDK { namespace certifiedSender { @@ -175,6 +177,23 @@ bool CertifiedSender::init() { m_powerResource->acquire(); } + /// Load stored messages from storage. + std::queue storedMessages; + if (!m_storage->load(&storedMessages)) { + ACSDK_ERROR(LX("initFailed").m("Could not load messages from database file.")); + return false; + } + + while (!storedMessages.empty() && static_cast(storedMessages.size()) <= m_queueSizeHardLimit) { + auto storedMessage = storedMessages.front(); + { + std::lock_guard lock{m_mutex}; + m_messagesToSend.push_back(std::make_shared( + storedMessage.message, storedMessage.id, storedMessage.uriPathExtension)); + } + storedMessages.pop(); + } + m_workerThread = std::thread(&CertifiedSender::mainloop, this); return true; diff --git a/CertifiedSender/src/SQLiteMessageStorage.cpp b/CertifiedSender/src/SQLiteMessageStorage.cpp index 6afe9e09b8..697c571107 100644 --- a/CertifiedSender/src/SQLiteMessageStorage.cpp +++ b/CertifiedSender/src/SQLiteMessageStorage.cpp @@ -53,14 +53,20 @@ static const std::string DATABASE_COLUMN_ID_NAME = "id"; static const std::string DATABASE_COLUMN_MESSAGE_TEXT_NAME = "message_text"; /// The name of the 'uriPathExtension' field corresponding to the uri path extension of the message. static const std::string DATABASE_COLUMN_URI = "uri"; - +/// The name of the 'timestamp' field is the creation time of the message. +static const std::string DATABASE_COLUMN_TIMESTAMP = "timestamp"; +/// The limit for the load() from the storage, it should be equal to CERTIFIED_SENDER_QUEUE_SIZE_WARN_LIMIT +static const std::string DATABASE_MESSAGE_SIZE_LIMIT = "25"; +/// The age limit of the message that can stay in the database +static const std::string DATABASE_MESSAGE_AGE_LIMIT = "5 minutes"; // clang-format off /// The SQL string to create the alerts table. static const std::string CREATE_MESSAGES_TABLE_SQL_STRING = std::string("CREATE TABLE ") + MESSAGES_TABLE_NAME + " (" + DATABASE_COLUMN_ID_NAME + " INT PRIMARY KEY NOT NULL," + DATABASE_COLUMN_URI + " TEXT NOT NULL," + - DATABASE_COLUMN_MESSAGE_TEXT_NAME + " TEXT NOT NULL);"; + DATABASE_COLUMN_MESSAGE_TEXT_NAME + " TEXT NOT NULL," + + DATABASE_COLUMN_TIMESTAMP + " DATETIME DEFAULT CURRENT_TIMESTAMP);" ; // clang-format on @@ -116,16 +122,50 @@ bool SQLiteMessageStorage::createDatabase() { bool SQLiteMessageStorage::open() { if (!m_database.open()) { + ACSDK_ERROR(LX("openFailed").d("reason", "Cannot open Certified Sender database")); return false; } // We need to check if the opened database contains the correct table. - if (!m_database.tableExists(MESSAGES_TABLE_NAME) && !m_database.performQuery(CREATE_MESSAGES_TABLE_SQL_STRING)) { - ACSDK_ERROR(LX("openFailed").d("sqlString", CREATE_MESSAGES_TABLE_SQL_STRING)); - close(); + // If the table does not exist and we can create a new table, + // the table is not legacy and it is a new table with new schema + if (!m_database.tableExists(MESSAGES_TABLE_NAME)) { + if (!m_database.performQuery(CREATE_MESSAGES_TABLE_SQL_STRING)) { + ACSDK_ERROR(LX("openFailed").d("sqlStatement", CREATE_MESSAGES_TABLE_SQL_STRING)); + close(); + return false; + } + + } + // If the table exists, check if it is legacy database. + else { + // If it is legacy, delete the table and create the new one + int database_status = isDatabaseLegacy(); + if (database_status == 1) { + if (!dropTable() || !m_database.performQuery(CREATE_MESSAGES_TABLE_SQL_STRING)) { + close(); + ACSDK_ERROR(LX("openFailed").d("database_status", "Cannot drop and create new database")); + return false; + } + } + // The database is pre-exist but errors in isDatabaseLegacy() + else if (database_status == -1) { + ACSDK_ERROR(LX("openFailed").d("database_status", "Pre-exist database but errors")); + return false; + } + } + + if (!eraseMessageOverAgeLimit()) { + ACSDK_ERROR(LX("openFailed").d("eraseMessageOverAgeLimit", "Cannot erase messages over age limit")); + return false; + } + + if (!eraseMessageOverSizeLimit()) { + ACSDK_ERROR(LX("openFailed").d("eraseMessageOverSizeLimit", "Cannot erase messages over size limit")); return false; } + // The database is pre-exist and not legacy return true; } @@ -267,5 +307,93 @@ bool SQLiteMessageStorage::clearDatabase() { return true; } +int SQLiteMessageStorage::isDatabaseLegacy() { + auto sqlStatement = m_database.createStatement("PRAGMA table_info(" + MESSAGES_TABLE_NAME + ");"); + + if ((!sqlStatement) || (!sqlStatement->step())) { + ACSDK_ERROR(LX("isDatabaseLegacy").d("reason", "failed checking legacy database")); + return -1; + } + + const std::string tableInfoColumnName = "name"; + + std::string columnName; + while (SQLITE_ROW == sqlStatement->getStepResult()) { + int columnCount = sqlStatement->getColumnCount(); + + for (int i = 0; i < columnCount; i++) { + std::string tableColumnName = sqlStatement->getColumnName(i); + + if (tableInfoColumnName == tableColumnName) { + columnName = sqlStatement->getColumnText(i); + if (DATABASE_COLUMN_TIMESTAMP == columnName) { + ACSDK_DEBUG9(LX("isDatabaseLegacy").d("reason", "databaseNotLegacy")); + return 0; + } + } + } + if (!sqlStatement->step()) { + ACSDK_ERROR(LX("isDatabaseLegacy").d("reason", "failed checking legacy database")); + return -1; + } + } + + ACSDK_INFO(LX("isDatabaseLegacy").d("reason", "legacy database found")); + return 1; +} + +bool SQLiteMessageStorage::dropTable() { + const std::string sqlString = "DROP TABLE IF EXISTS " + MESSAGES_TABLE_NAME + ";"; + + auto statement = m_database.performQuery(sqlString); + + if (!statement) { + ACSDK_ERROR(LX("dropTableFailed").m("could not drop messages table.")); + return false; + } + + return true; +} + +bool SQLiteMessageStorage::eraseMessageOverAgeLimit() { + std::string sqlString = "DELETE FROM " + MESSAGES_TABLE_NAME + " WHERE DATETIME('now', '-" + + DATABASE_MESSAGE_AGE_LIMIT + "') >= " + DATABASE_COLUMN_TIMESTAMP + ";"; + + auto statement = m_database.createStatement(sqlString); + + if (!statement) { + ACSDK_ERROR(LX("eraseMessageOverAgeLimitFailed").m("Could not create statement.")); + return false; + } + + if (!statement->step()) { + ACSDK_ERROR(LX("eraseMessageOverAgeLimitFailed").m("Could not perform step.")); + return false; + } + + return true; +} + +bool SQLiteMessageStorage::eraseMessageOverSizeLimit() { + std::string sqlString = "DELETE FROM " + MESSAGES_TABLE_NAME + " WHERE " + DATABASE_COLUMN_ID_NAME + + " NOT IN ( SELECT " + DATABASE_COLUMN_ID_NAME + " FROM " + MESSAGES_TABLE_NAME + + " ORDER BY " + DATABASE_COLUMN_ID_NAME + " DESC LIMIT " + DATABASE_MESSAGE_SIZE_LIMIT + + " ); "; + + auto statement = m_database.createStatement(sqlString); + + if (!statement) { + ACSDK_ERROR(LX("eraseMessageOverSizeLimit").m("Could not create statement.")); + return false; + } + + if (!statement->step()) { + ACSDK_ERROR(LX("eraseMessageOverSizeLimit").m("Could not perform step.")); + return false; + } + + return true; +} + } // namespace certifiedSender } // namespace alexaClientSDK diff --git a/CertifiedSender/test/CMakeLists.txt b/CertifiedSender/test/CMakeLists.txt index d73cc67df7..167d94b94d 100644 --- a/CertifiedSender/test/CMakeLists.txt +++ b/CertifiedSender/test/CMakeLists.txt @@ -4,6 +4,6 @@ set(INCLUDE_PATH "${AVSCommon_INCLUDE_DIRS}" "${CertifiedSender_INCLUDE_DIRS}") -set(TEST_FOLDER "${CertifiedSender_SOURCE_DIR}/test") +set(TEST_FOLDER "${CertifiedSender_BINARY_DIR}/test") discover_unit_tests("${INCLUDE_PATH}" "CertifiedSender;SDKInterfacesTests" "${TEST_FOLDER}") \ No newline at end of file diff --git a/CertifiedSender/test/CertifiedSenderTest.cpp b/CertifiedSender/test/CertifiedSenderTest.cpp index cdbdc57717..9ea925ce43 100644 --- a/CertifiedSender/test/CertifiedSenderTest.cpp +++ b/CertifiedSender/test/CertifiedSenderTest.cpp @@ -14,8 +14,8 @@ */ #include -#include #include +#include #include @@ -87,20 +87,22 @@ class CertifiedSenderTest : public ::testing::Test { (*configuration) << CONFIGURATION; ASSERT_TRUE(avsCommon::avs::initialization::AlexaClientSDKInit::initialize({configuration})); - auto customerDataManager = std::make_shared(); + m_customerDataManager = std::make_shared(); m_mockMessageSender = std::make_shared(); m_connection = std::make_shared(); m_storage = std::make_shared(); EXPECT_CALL(*m_storage, open()).Times(1).WillOnce(Return(true)); - m_certifiedSender = CertifiedSender::create(m_mockMessageSender, m_connection, m_storage, customerDataManager); + EXPECT_CALL(*m_storage, load(_)).Times(1).WillOnce(Return(true)); + m_certifiedSender = + CertifiedSender::create(m_mockMessageSender, m_connection, m_storage, m_customerDataManager); } void TearDown() override { if (avsCommon::avs::initialization::AlexaClientSDKInit::isInitialized()) { avsCommon::avs::initialization::AlexaClientSDKInit::uninitialize(); } - m_connection->removeConnectionStatusObserver(m_certifiedSender); + m_certifiedSender->shutdown(); } /// Class under test. @@ -113,6 +115,9 @@ class CertifiedSenderTest : public ::testing::Test { /// be deleted. std::shared_ptr m_connection; + /// The pointer to the customer data manager + std::shared_ptr m_customerDataManager; + /// The mock message sender instance. std::shared_ptr m_mockMessageSender; }; @@ -125,17 +130,100 @@ TEST_F(CertifiedSenderTest, test_clearData) { m_certifiedSender->clearData(); } +/** + * Tests various failure scenarios for the init method. + */ +TEST_F(CertifiedSenderTest, test_initFailsWhenStorageMethodsFail) { + /// Test if the init method fails when createDatabase on storage fails. + { + EXPECT_CALL(*m_storage, open()).Times(1).WillOnce(Return(false)); + EXPECT_CALL(*m_storage, createDatabase()).Times(1).WillOnce(Return(false)); + EXPECT_CALL(*m_storage, load(_)).Times(0); + auto certifiedSender = + CertifiedSender::create(m_mockMessageSender, m_connection, m_storage, m_customerDataManager); + ASSERT_EQ(certifiedSender, nullptr); + } + + /// Test if the init method fails when load from storage fails. + { + EXPECT_CALL(*m_storage, open()).Times(1).WillOnce(Return(true)); + EXPECT_CALL(*m_storage, load(_)).Times(1).WillOnce(Return(false)); + auto certifiedSender = + CertifiedSender::create(m_mockMessageSender, m_connection, m_storage, m_customerDataManager); + ASSERT_EQ(certifiedSender, nullptr); + } +} + +/** + * Tests if the stored messages get sent when a connection is established. + */ +TEST_F(CertifiedSenderTest, testTimer_storedMessagesGetSent) { + EXPECT_CALL(*m_storage, open()).Times(1).WillOnce(Return(true)); + + /// Return messages from storage. + EXPECT_CALL(*m_storage, load(_)) + .Times(1) + .WillOnce(Invoke([](std::queue* storedMessages) { + storedMessages->push(MessageStorageInterface::StoredMessage(1, "testMessage_1")); + storedMessages->push(MessageStorageInterface::StoredMessage(2, "testMessage_2")); + return true; + })); + + avsCommon::utils::PromiseFuturePair allRequestsSent; + { + InSequence s; + + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)) + .Times(1) + .WillOnce(Invoke([](std::shared_ptr request) { + ASSERT_EQ(request->getJsonContent(), "testMessage_1"); + request->sendCompleted(avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS); + })); + EXPECT_CALL(*m_storage, erase(1)).WillOnce(Return(true)); + + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)) + .Times(1) + .WillOnce(Invoke([&allRequestsSent](std::shared_ptr request) { + ASSERT_EQ(request->getJsonContent(), "testMessage_2"); + request->sendCompleted(avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS); + allRequestsSent.setValue(true); + })); + EXPECT_CALL(*m_storage, erase(2)).WillOnce(Return(true)); + } + + auto certifiedSender = CertifiedSender::create(m_mockMessageSender, m_connection, m_storage, m_customerDataManager); + + std::static_pointer_cast(certifiedSender) + ->onConnectionStatusChanged( + avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status::CONNECTED, + avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::ChangedReason::SUCCESS); + + /// wait for requests to get sent out. + EXPECT_TRUE(allRequestsSent.waitFor(TEST_TIMEOUT)); + + /// Cleanup + certifiedSender->shutdown(); +} + /** * Verify that a message with a URI specified will be sent out by the sender with the URI. */ TEST_F(CertifiedSenderTest, testTimer_SendMessageWithURI) { avsCommon::utils::PromiseFuturePair> requestSent; - EXPECT_CALL(*m_mockMessageSender, sendMessage(_)) - .WillOnce(Invoke([&requestSent](std::shared_ptr request) { - requestSent.setValue(request); - })); + EXPECT_CALL(*m_storage, store(_, TEST_URI, _)).WillOnce(Return(true)); + { + InSequence s; + EXPECT_CALL(*m_mockMessageSender, sendMessage(_)) + .WillOnce(Invoke([&requestSent](std::shared_ptr request) { + requestSent.setValue(request); + request->sendCompleted(avsCommon::sdkInterfaces::MessageRequestObserverInterface::Status::SUCCESS); + })); + + EXPECT_CALL(*m_storage, erase(_)).WillOnce(Return(true)); + } + std::static_pointer_cast(m_certifiedSender) ->onConnectionStatusChanged( avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status::CONNECTED, diff --git a/CertifiedSender/test/MessageStorageTest.cpp b/CertifiedSender/test/MessageStorageTest.cpp index 27a5368d70..10265e5eac 100644 --- a/CertifiedSender/test/MessageStorageTest.cpp +++ b/CertifiedSender/test/MessageStorageTest.cpp @@ -17,7 +17,7 @@ #include #include - +#include #include #include @@ -34,8 +34,6 @@ using namespace avsCommon::utils::file; /// The filename we will use for the test database file. static const std::string TEST_DATABASE_FILE_PATH = "messageStorageTestDatabase.db"; -/// The filename we will use for the test database file. -static const std::string TEST_SECOND_DATABASE_FILE_PATH = "messageStorageSecondTestDatabase.db"; /// The path delimiter used by the OS to identify file locations. static const std::string PATH_DELIMITER = "/"; /// The full filepath to the database file we will create and delete during tests. @@ -48,7 +46,20 @@ static const std::string TEST_MESSAGE_TWO = "test_message_two"; static const std::string TEST_MESSAGE_THREE = "test_message_three"; /// A test message uri. static const std::string TEST_MESSAGE_URI = "/v20160207/events/SpeechRecognizer/Recognize"; - +/// The name of the alerts table. +static const std::string MESSAGES_TABLE_NAME = "messages_with_uri"; +/// The name of the 'id' field we will use as the primary key in our tables. +static const std::string DATABASE_COLUMN_ID_NAME = "id"; +/// The name of the 'message_text' field we will use as the primary key in our tables. +static const std::string DATABASE_COLUMN_MESSAGE_TEXT_NAME = "message_text"; +/// The name of the 'uriPathExtension' field corresponding to the uri path extension of the message. +static const std::string DATABASE_COLUMN_URI = "uri"; +/// The name of the 'timestamp' field is the creation time of the message. +static const std::string DATABASE_COLUMN_TIMESTAMP = "timestamp"; +/// The SQL string to create the alerts table. +static const std::string CREATE_LEGACY_MESSAGES_TABLE_SQL_STRING = + std::string("CREATE TABLE ") + MESSAGES_TABLE_NAME + " (" + DATABASE_COLUMN_ID_NAME + " INT PRIMARY KEY NOT NULL," + + DATABASE_COLUMN_URI + " TEXT NOT NULL," + DATABASE_COLUMN_MESSAGE_TEXT_NAME + " TEXT NOT NULL);"; /** * A class which helps drive this unit test suite. */ @@ -57,41 +68,48 @@ class MessageStorageTest : public ::testing::Test { /** * Constructor. */ - MessageStorageTest() : m_storage{std::make_shared(g_dbTestFilePath)} { - cleanupLocalDbFile(); - } + MessageStorageTest(); /** * Destructor. */ - ~MessageStorageTest() { - m_storage->close(); - cleanupLocalDbFile(); - } + ~MessageStorageTest(); /** * Utility function to create the database, using the global filename. */ - void createDatabase() { - m_storage->createDatabase(); - } + void createDatabase(); + + /** + * Utility function to create legacy database that does not have timestamp column + */ + bool createLegacyDatabase(); /** * Utility function to cleanup the test database file, if it exists. */ - void cleanupLocalDbFile() { - if (g_dbTestFilePath.empty()) { - return; - } + void cleanupLocalDbFile(); - if (fileExists(g_dbTestFilePath)) { - removeFile(g_dbTestFilePath.c_str()); - } - } + /** + * Utility function to check if the current table is legacy or not + * EXPECT: 1 = DB is legacy, 0 = DB is not legacy, -1 = errors + */ + int isDatabaseLegacy(); + + /** + * Utility function to insert old messages in the storage + */ + bool insertOldMessagesToDatabase(int id, const std::string& record_age); + + /** + * Utility function to create old messages in the storage + */ + bool createOldMessages(); protected: /// The message database object we will test. std::shared_ptr m_storage; + std::unique_ptr m_legacyDB; }; /** @@ -105,6 +123,126 @@ static bool isOpen(const std::shared_ptr& storage) { return storage->load(&dummyMessages); } +MessageStorageTest::MessageStorageTest() : m_storage{std::make_shared(g_dbTestFilePath)} { + cleanupLocalDbFile(); +} + +MessageStorageTest::~MessageStorageTest() { + m_storage->close(); + if (m_legacyDB) { + m_legacyDB->close(); + } + cleanupLocalDbFile(); +} + +void MessageStorageTest::createDatabase() { + m_storage->createDatabase(); +} + +bool MessageStorageTest::createLegacyDatabase() { + m_legacyDB = std::unique_ptr( + new alexaClientSDK::storage::sqliteStorage::SQLiteDatabase(g_dbTestFilePath)); + + if (!m_legacyDB || !m_legacyDB->initialize()) { + return false; + } + + if (!m_legacyDB->performQuery(CREATE_LEGACY_MESSAGES_TABLE_SQL_STRING)) { + m_legacyDB->close(); + return false; + } + + return true; +} + +void MessageStorageTest::cleanupLocalDbFile() { + if (g_dbTestFilePath.empty()) { + return; + } + + if (fileExists(g_dbTestFilePath)) { + removeFile(g_dbTestFilePath.c_str()); + } +} + +int MessageStorageTest::isDatabaseLegacy() { + auto sqlStatement = m_legacyDB->createStatement("PRAGMA table_info(" + MESSAGES_TABLE_NAME + ");"); + + if ((!sqlStatement) || (!sqlStatement->step())) { + return -1; + } + + const std::string tableInfoColumnName = "name"; + + std::string columnName; + while (SQLITE_ROW == sqlStatement->getStepResult()) { + int columnCount = sqlStatement->getColumnCount(); + + for (int i = 0; i < columnCount; i++) { + std::string tableColumnName = sqlStatement->getColumnName(i); + + if (tableInfoColumnName == tableColumnName) { + columnName = sqlStatement->getColumnText(i); + if (DATABASE_COLUMN_TIMESTAMP == columnName) { + return 0; + } + } + } + + if (!sqlStatement->step()) { + return -1; + } + } + return 1; +} + +bool MessageStorageTest::insertOldMessagesToDatabase(int id, const std::string& record_age) { + if (!m_legacyDB) { + return false; + } + + std::string sqlString = std::string("INSERT INTO " + MESSAGES_TABLE_NAME + " (") + DATABASE_COLUMN_ID_NAME + ", " + + DATABASE_COLUMN_URI + ", " + DATABASE_COLUMN_MESSAGE_TEXT_NAME + ", " + + DATABASE_COLUMN_TIMESTAMP + ") VALUES (?, ?, ?, datetime('now', ?));"; + + auto statement = m_legacyDB->createStatement(sqlString); + int boundParam = 1; + std::string uriPathExtension = "testURI"; + std::string message = "testURI"; + if (!statement->bindIntParameter(boundParam++, id) || + !statement->bindStringParameter(boundParam++, uriPathExtension) || + !statement->bindStringParameter(boundParam++, message) || + !statement->bindStringParameter(boundParam, record_age)) { + return false; + } + if (!statement->step()) { + return false; + } + + return true; +} + +bool MessageStorageTest::createOldMessages() { + m_legacyDB = std::unique_ptr( + new alexaClientSDK::storage::sqliteStorage::SQLiteDatabase(g_dbTestFilePath)); + + m_legacyDB->open(); + // Insert 60 records 1 month ago + for (int id = 1; id <= 60; ++id) { + if (!insertOldMessagesToDatabase(id, "-1 months")) { + return false; + } + } + + // Insert 60 record at this moment + for (int id = 61; id <= 120; ++id) { + if (!insertOldMessagesToDatabase(id, "-0 seconds")) { + return false; + } + } + + return true; +} /** * Test basic construction. Database should not be open. */ @@ -231,7 +369,7 @@ TEST_F(MessageStorageTest, test_databaseClear) { /** * Test storing records with URI in the database. */ -TEST_F(MessageStorageTest, testDatabaseStoreAndLoadWithURI) { +TEST_F(MessageStorageTest, test_DatabaseStoreAndLoadWithURI) { createDatabase(); EXPECT_TRUE(isOpen(m_storage)); @@ -247,6 +385,54 @@ TEST_F(MessageStorageTest, testDatabaseStoreAndLoadWithURI) { EXPECT_EQ(dbMessages.front().uriPathExtension, TEST_MESSAGE_URI); } +/** + * Test legacy database + */ +TEST_F(MessageStorageTest, test_LegacyDatabase) { + EXPECT_TRUE(createLegacyDatabase()); + EXPECT_EQ(isDatabaseLegacy(), 1); + + // Perform opening it to change it to new database + // and check if it is legacy and it works + EXPECT_TRUE(m_storage->open()); + EXPECT_TRUE(isOpen(m_storage)); + EXPECT_EQ(isDatabaseLegacy(), 0); + + // Close it and check the file + m_storage->close(); + EXPECT_FALSE(isOpen(m_storage)); +} + +/** + * Test erase legacy message + */ +TEST_F(MessageStorageTest, test_EraseMessageOverAgeAndSizeLimit) { + createDatabase(); + // Create old messages over age and database size limit + EXPECT_TRUE(createOldMessages()); + + std::queue dbMessagesBefore; + EXPECT_TRUE(m_storage->load(&dbMessagesBefore)); + EXPECT_EQ(static_cast(dbMessagesBefore.size()), 120); + + // The mocking database opens and deletes over age and size limit + m_storage->close(); + EXPECT_TRUE(m_storage->open()); + EXPECT_TRUE(isOpen(m_storage)); + + std::queue dbMessagesAfter; + EXPECT_TRUE(m_storage->load(&dbMessagesAfter)); + // eraseMessageOverAgeLimit() takes out the first 60 inserted 1 month ago + // eraseMessageOverSizeLimit() takes out the next 35 messages + + EXPECT_EQ(static_cast(dbMessagesAfter.size()), 25); + for (int id = 96; id <= 120; ++id) { + EXPECT_EQ(static_cast(dbMessagesAfter.front().id), id); + dbMessagesAfter.pop(); + } + EXPECT_EQ(static_cast(dbMessagesAfter.size()), 0); +} + } // namespace test } // namespace certifiedSender } // namespace alexaClientSDK diff --git a/ContextManager/CMakeLists.txt b/ContextManager/CMakeLists.txt index 1217ce7699..d18337869d 100644 --- a/ContextManager/CMakeLists.txt +++ b/ContextManager/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(ContextManager LANGUAGES CXX) -include(../build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/ContextManager/src/ContextManager.cpp b/ContextManager/src/ContextManager.cpp index c1374f0038..a1f11ab796 100644 --- a/ContextManager/src/ContextManager.cpp +++ b/ContextManager/src/ContextManager.cpp @@ -381,6 +381,7 @@ ContextRequestToken ContextManager::getContextInternal( std::function contextAvailableCallback = NoopCallback; { std::lock_guard statesLock{m_endpointsStateMutex}; + for (auto& capability : m_endpointsState[requestEndpointId]) { auto stateInfo = capability.second; auto stateProvider = capability.second.stateProvider; @@ -419,7 +420,7 @@ ContextRequestToken ContextManager::getContextInternal( std::function ContextManager::getContextFailureCallbackLocked( unsigned int requestToken, ContextRequestError error) { - ACSDK_DEBUG5(LX(__func__).d("token", requestToken)); + ACSDK_ERROR(LX(__func__).d("token", requestToken).d("error", error)); // Make sure the request gets cleared in the end of this function no matter the outcome. error::FinallyGuard clearRequestGuard{[this, requestToken] { auto requestIt = m_pendingRequests.find(requestToken); @@ -436,6 +437,9 @@ std::function ContextManager::getContextFailureCallbackLocked( return NoopCallback; } for (auto& pendingState : m_pendingStateRequest[requestToken]) { + ACSDK_ERROR(LX(__func__) + .d("pendingStateProviderName", pendingState.name) + .d("pendingStateProviderNamespace", pendingState.nameSpace)); auto metricName = STATE_PROVIDER_TIMEOUT_METRIC_PREFIX + pendingState.nameSpace; recordMetric( m_metricRecorder, diff --git a/Diagnostics/CMakeLists.txt b/Diagnostics/CMakeLists.txt index 301ded0741..2ca63c9b71 100644 --- a/Diagnostics/CMakeLists.txt +++ b/Diagnostics/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(Diagnostics LANGUAGES CXX) -include(../build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/Endpoints/CMakeLists.txt b/Endpoints/CMakeLists.txt index 640ff85325..f50eafaed6 100644 --- a/Endpoints/CMakeLists.txt +++ b/Endpoints/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(Endpoints LANGUAGES CXX) -include(../build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/Endpoints/include/Endpoints/EndpointRegistrationManager.h b/Endpoints/include/Endpoints/EndpointRegistrationManager.h index 5330755983..1030054658 100644 --- a/Endpoints/include/Endpoints/EndpointRegistrationManager.h +++ b/Endpoints/include/Endpoints/EndpointRegistrationManager.h @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include #include @@ -92,7 +92,7 @@ class EndpointRegistrationManager : public avsCommon::sdkInterfaces::endpoints:: /** * Class used to observe changes to the capabilities registration. */ - class CapabilityRegistrationProxy : public avsCommon::sdkInterfaces::CapabilitiesObserverInterface { + class CapabilityRegistrationProxy : public avsCommon::sdkInterfaces::CapabilitiesDelegateObserverInterface { public: /** * Sets the callback function. @@ -104,7 +104,7 @@ class EndpointRegistrationManager : public avsCommon::sdkInterfaces::endpoints:: const std::pair>& addedOrUpdatedEndpoints, const std::pair>& deletedEndpoints)> callback); - /// @name CapabilitiesObserverInterface methods. + /// @name CapabilitiesDelegateObserverInterface methods. /// @{ void onCapabilitiesStateChange( State newState, diff --git a/Endpoints/src/EndpointBuilder.cpp b/Endpoints/src/EndpointBuilder.cpp index 13fdfc0ec4..9d8bfc0aad 100644 --- a/Endpoints/src/EndpointBuilder.cpp +++ b/Endpoints/src/EndpointBuilder.cpp @@ -56,8 +56,8 @@ static const std::string TAG("EndpointBuilder"); /// String used to join attributes in the generation of the derived endpoint id. const std::string ENDPOINT_ID_CONCAT = "::"; -/// We will limit the suffix length to 10 characters for now to ensure that we don't go over the endpointId length. -static constexpr size_t MAX_SUFFIX_LENGTH = 10; +/// We will limit the EndpointId length to 256 characters +static constexpr size_t MAX_ENDPOINTID_LENGTH = 256; /// The display category for the AVS device endpoint; const std::string ALEXA_DISPLAY_CATEGORY = "ALEXA_VOICE_ENABLED"; @@ -134,10 +134,6 @@ void EndpointBuilder::configureDefaultEndpoint() { m_deviceInfo->getDeviceSerialNumber(), m_deviceInfo->getRegistrationKey(), m_deviceInfo->getProductIdKey())); - - if (!m_deviceInfo->getFriendlyName().empty()) { - m_attributes.friendlyName = m_deviceInfo->getFriendlyName(); - } } EndpointBuilder& EndpointBuilder::withDerivedEndpointId(const std::string& suffix) { @@ -146,12 +142,15 @@ EndpointBuilder& EndpointBuilder::withDerivedEndpointId(const std::string& suffi return *this; } - if (suffix.length() > MAX_SUFFIX_LENGTH) { - ACSDK_ERROR(LX(std::string(__func__) + "Failed").d("reason", "suffixMaxLengthExceeded").d("suffix", suffix)); + std::string fullEndpointId = m_deviceInfo->getDefaultEndpointId() + ENDPOINT_ID_CONCAT + suffix; + if (fullEndpointId.length() > MAX_ENDPOINTID_LENGTH) { + ACSDK_ERROR(LX(std::string(__func__) + "Failed") + .d("reason", "EndpointIdMaxLengthExceeded") + .sensitive("fullEndpointId", fullEndpointId)); return *this; } - m_attributes.endpointId = m_deviceInfo->getDefaultEndpointId() + ENDPOINT_ID_CONCAT + suffix; + m_attributes.endpointId = fullEndpointId; return *this; } diff --git a/Endpoints/src/EndpointRegistrationManager.cpp b/Endpoints/src/EndpointRegistrationManager.cpp index bad1eed4dc..879fc7783a 100644 --- a/Endpoints/src/EndpointRegistrationManager.cpp +++ b/Endpoints/src/EndpointRegistrationManager.cpp @@ -392,16 +392,16 @@ EndpointRegistrationManager::~EndpointRegistrationManager() { } void EndpointRegistrationManager::CapabilityRegistrationProxy::onCapabilitiesStateChange( - CapabilitiesObserverInterface::State newState, - CapabilitiesObserverInterface::Error newError, + CapabilitiesDelegateObserverInterface::State newState, + CapabilitiesDelegateObserverInterface::Error newError, const std::vector& addedOrUpdatedEndpointIds, const std::vector& deletedEndpointIds) { ACSDK_DEBUG5(LX(__func__).d("state", newState).d("error", newError).d("callback", static_cast(m_callback))); - if (m_callback && (CapabilitiesObserverInterface::State::RETRIABLE_ERROR != newState)) { - auto registrationResult = (CapabilitiesObserverInterface::State::SUCCESS == newState) + if (m_callback && (CapabilitiesDelegateObserverInterface::State::RETRIABLE_ERROR != newState)) { + auto registrationResult = (CapabilitiesDelegateObserverInterface::State::SUCCESS == newState) ? RegistrationResult::SUCCEEDED : RegistrationResult::CONFIGURATION_ERROR; - auto deregistrationResult = (CapabilitiesObserverInterface::State::SUCCESS == newState) + auto deregistrationResult = (CapabilitiesDelegateObserverInterface::State::SUCCESS == newState) ? DeregistrationResult::SUCCEEDED : DeregistrationResult::CONFIGURATION_ERROR; m_callback( diff --git a/Endpoints/test/EndpointRegistrationManagerTest.cpp b/Endpoints/test/EndpointRegistrationManagerTest.cpp index 0910c05bcd..ced0712318 100644 --- a/Endpoints/test/EndpointRegistrationManagerTest.cpp +++ b/Endpoints/test/EndpointRegistrationManagerTest.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -84,7 +84,7 @@ class EndpointRegistrationManagerTest : public Test { std::shared_ptr> m_sequencer; std::shared_ptr> m_capabilitiesDelegate; std::shared_ptr> m_registrationObserver; - std::shared_ptr m_capabilitiesObserver; + std::shared_ptr m_capabilitiesObserver; std::unique_ptr m_manager; }; @@ -94,8 +94,9 @@ void EndpointRegistrationManagerTest::SetUp() { m_registrationObserver = std::make_shared>(); EXPECT_CALL(*m_capabilitiesDelegate, addCapabilitiesObserver(_)) - .WillOnce(Invoke( - [this](std::shared_ptr observer) { m_capabilitiesObserver = observer; })); + .WillOnce(Invoke([this](std::shared_ptr observer) { + m_capabilitiesObserver = observer; + })); m_manager = EndpointRegistrationManager::create(m_sequencer, m_capabilitiesDelegate, DEFAULT_ENDPOINT_ID); m_manager->addObserver(m_registrationObserver); @@ -163,8 +164,8 @@ TEST_F(EndpointRegistrationManagerTest, test_shutdownResolvesPendingPromises) { // Successfully add an endpoint so we can test resolving the pending delete on shutdown. auto result = m_manager->registerEndpoint(endpointToDelete); m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, - CapabilitiesObserverInterface::Error::SUCCESS, + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, {endpointIdToDelete}, {}); ASSERT_EQ(result.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); @@ -216,7 +217,10 @@ TEST_F(EndpointRegistrationManagerTest, test_registerEndpointSucceeds) { EXPECT_CALL(*m_registrationObserver, onEndpointRegistration(endpointId, _, RegistrationResult::SUCCEEDED)); m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, CapabilitiesObserverInterface::Error::SUCCESS, {endpointId}, {}); + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, + {endpointId}, + {}); ASSERT_EQ(result.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); EXPECT_EQ(result.get(), RegistrationResult::SUCCEEDED); } @@ -246,7 +250,10 @@ TEST_F(EndpointRegistrationManagerTest, test_deregisterEndpointSucceeds) { EXPECT_CALL(*m_registrationObserver, onEndpointRegistration(endpointId, _, RegistrationResult::SUCCEEDED)); m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, CapabilitiesObserverInterface::Error::SUCCESS, {endpointId}, {}); + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, + {endpointId}, + {}); ASSERT_EQ(addResult.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); EXPECT_EQ(addResult.get(), RegistrationResult::SUCCEEDED); @@ -255,7 +262,10 @@ TEST_F(EndpointRegistrationManagerTest, test_deregisterEndpointSucceeds) { auto deleteResult = m_manager->deregisterEndpoint(endpointId); EXPECT_CALL(*m_registrationObserver, onEndpointDeregistration(endpointId, DeregistrationResult::SUCCEEDED)); m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, CapabilitiesObserverInterface::Error::SUCCESS, {}, {endpointId}); + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, + {}, + {endpointId}); ASSERT_EQ(deleteResult.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); EXPECT_EQ(deleteResult.get(), DeregistrationResult::SUCCEEDED); } @@ -292,8 +302,8 @@ TEST_F(EndpointRegistrationManagerTest, test_modifyDefaultEndpointFails) { ASSERT_EQ(result.wait_for(std::chrono::milliseconds::zero()), std::future_status::timeout); m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, - CapabilitiesObserverInterface::Error::SUCCESS, + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, {DEFAULT_ENDPOINT_ID}, {}); ASSERT_EQ(result.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); @@ -343,8 +353,8 @@ TEST_F(EndpointRegistrationManagerTest, test_registerEndpointWhenCapabilityRegis *m_registrationObserver, onEndpointRegistration(endpointId, _, RegistrationResult::CONFIGURATION_ERROR)); m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::FATAL_ERROR, - CapabilitiesObserverInterface::Error::UNKNOWN_ERROR, + CapabilitiesDelegateObserverInterface::State::FATAL_ERROR, + CapabilitiesDelegateObserverInterface::Error::UNKNOWN_ERROR, {endpointId}, {}); ASSERT_EQ(result.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); @@ -427,7 +437,10 @@ TEST_F(EndpointRegistrationManagerTest, test_registerEndpointWhileDeregistration // Check that register endpoint succeeded. auto result = m_manager->registerEndpoint(endpoint); m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, CapabilitiesObserverInterface::Error::SUCCESS, {endpointId}, {}); + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, + {endpointId}, + {}); ASSERT_EQ(result.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); ASSERT_EQ(result.get(), RegistrationResult::SUCCEEDED); @@ -468,7 +481,10 @@ TEST_F(EndpointRegistrationManagerTest, test_deregisterEndpointWhileDeregistrati // Check that register endpoint succeeded. auto result = m_manager->registerEndpoint(endpoint); m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, CapabilitiesObserverInterface::Error::SUCCESS, {endpointId}, {}); + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, + {endpointId}, + {}); ASSERT_EQ(result.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); ASSERT_EQ(result.get(), RegistrationResult::SUCCEEDED); @@ -601,7 +617,10 @@ TEST_F(EndpointRegistrationManagerTest, test_registerExistingEndpointSucceeds) { ASSERT_EQ(result.wait_for(std::chrono::milliseconds::zero()), std::future_status::timeout); m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, CapabilitiesObserverInterface::Error::SUCCESS, {endpointId}, {}); + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, + {endpointId}, + {}); ASSERT_EQ(result.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); EXPECT_EQ(result.get(), RegistrationResult::SUCCEEDED); @@ -610,7 +629,10 @@ TEST_F(EndpointRegistrationManagerTest, test_registerExistingEndpointSucceeds) { ASSERT_EQ(updateResult.wait_for(std::chrono::milliseconds::zero()), std::future_status::timeout); m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, CapabilitiesObserverInterface::Error::SUCCESS, {endpointId}, {}); + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, + {endpointId}, + {}); ASSERT_EQ(updateResult.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); EXPECT_EQ(updateResult.get(), RegistrationResult::SUCCEEDED); } @@ -662,7 +684,10 @@ TEST_F( ASSERT_EQ(result.wait_for(std::chrono::milliseconds::zero()), std::future_status::timeout); m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, CapabilitiesObserverInterface::Error::SUCCESS, {endpointId}, {}); + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, + {endpointId}, + {}); ASSERT_EQ(result.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); EXPECT_EQ(result.get(), RegistrationResult::SUCCEEDED); @@ -672,8 +697,8 @@ TEST_F( // Fail the update. m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::FATAL_ERROR, - CapabilitiesObserverInterface::Error::UNKNOWN_ERROR, + CapabilitiesDelegateObserverInterface::State::FATAL_ERROR, + CapabilitiesDelegateObserverInterface::Error::UNKNOWN_ERROR, {endpointId}, {}); ASSERT_EQ(updateResult.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); @@ -722,7 +747,10 @@ TEST_F(EndpointRegistrationManagerTest, test_revertWhenRegisterExistingEndpointF ASSERT_EQ(result.wait_for(std::chrono::milliseconds::zero()), std::future_status::timeout); m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, CapabilitiesObserverInterface::Error::SUCCESS, {endpointId}, {}); + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, + {endpointId}, + {}); ASSERT_EQ(result.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); EXPECT_EQ(result.get(), RegistrationResult::SUCCEEDED); @@ -768,7 +796,10 @@ TEST_F( ASSERT_EQ(result.wait_for(std::chrono::milliseconds::zero()), std::future_status::timeout); m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, CapabilitiesObserverInterface::Error::SUCCESS, {endpointId}, {}); + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, + {endpointId}, + {}); ASSERT_EQ(result.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); EXPECT_EQ(result.get(), RegistrationResult::SUCCEEDED); @@ -777,8 +808,8 @@ TEST_F( ASSERT_EQ(deleteResult.wait_for(std::chrono::milliseconds::zero()), std::future_status::timeout); m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::FATAL_ERROR, - CapabilitiesObserverInterface::Error::UNKNOWN_ERROR, + CapabilitiesDelegateObserverInterface::State::FATAL_ERROR, + CapabilitiesDelegateObserverInterface::Error::UNKNOWN_ERROR, {}, {endpointId}); ASSERT_EQ(deleteResult.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); @@ -819,7 +850,10 @@ TEST_F(EndpointRegistrationManagerTest, test_revertWhenDeregisterEndpointFailsDu ASSERT_EQ(result.wait_for(std::chrono::milliseconds::zero()), std::future_status::timeout); m_capabilitiesObserver->onCapabilitiesStateChange( - CapabilitiesObserverInterface::State::SUCCESS, CapabilitiesObserverInterface::Error::SUCCESS, {endpointId}, {}); + CapabilitiesDelegateObserverInterface::State::SUCCESS, + CapabilitiesDelegateObserverInterface::Error::SUCCESS, + {endpointId}, + {}); ASSERT_EQ(result.wait_for(MY_WAIT_TIMEOUT), std::future_status::ready); EXPECT_EQ(result.get(), RegistrationResult::SUCCEEDED); diff --git a/Integration/AlexaClientSDKConfig.json b/Integration/AlexaClientSDKConfig.json index dff353864f..c45bd41995 100644 --- a/Integration/AlexaClientSDKConfig.json +++ b/Integration/AlexaClientSDKConfig.json @@ -1,61 +1,33 @@ +/// This configuration file contains the minimum configurations required for the SDK Sample App. +/// +/// More configuration options are available, which are not mentioned here. +/// See https://developer.amazon.com/en-US/docs/alexa/avs-device-sdk/alexa-client-sdk-config-json.html +/// to learn more about configuration options and default behaviours. { "cblAuthDelegate":{ - // Path to CBLAuthDelegate's database file. e.g. /home/ubuntu/Build/cblAuthDelegate.db - // Note: The directory specified must be valid. - // The database file (cblAuthDelegate.db) will be created by SampleApp, do not create it yourself. - // The database file should only be used for CBLAuthDelegate (don't use it for other components of SDK) "databaseFilePath":"${SDK_CBL_AUTH_DELEGATE_DATABASE_FILE_PATH}" }, "deviceInfo":{ - // Unique device serial number. e.g. 123456 "deviceSerialNumber":"${SDK_CONFIG_DEVICE_SERIAL_NUMBER}", - // The Client ID of the Product from developer.amazon.com "clientId":"${SDK_CONFIG_CLIENT_ID}", - // Product ID from developer.amazon.com "productId":"${SDK_CONFIG_PRODUCT_ID}", - // The name of the device manufacturer. "manufacturerName": "${SDK_CONFIG_MANUFACTURER_NAME}", - // The description of the device which should contain the manufacturer name or how the device is connected. "description": "${SDK_CONFIG_DEVICE_DESCRIPTION}" }, - "avsGatewayManager": { - // Allows configuring the AVS Gateway the device should connect to. - // Important Note: The AVSGatewayManager stores a previously verified gateway in the MiscDB and uses it - // to establish a connection with AVS. In order to override the gateway configured here, you may have to - // delete the AVSGatewayManager table under the MiscDB. - // "avsGateway":"https://alexa.na.gateway.devices.a2z.com" - }, "capabilitiesDelegate":{ - // Path to misc database file. e.g. /home/ubuntu/Build/capabilitiesDelegate.db - // Note: The directory specified must be valid. - // The database file (capabilitiesDelegate.db) will be created by SampleApp, do not create it yourself. "databaseFilePath":"${SDK_CAPABILITIES_DELEGATE_DATABASE_FILE_PATH}" }, "miscDatabase":{ - // Path to misc database file. e.g. /home/ubuntu/Build/miscDatabase.db - // Note: The directory specified must be valid. - // The database file (miscDatabase.db) will be created by SampleApp, do not create it yourself. "databaseFilePath":"${SDK_MISC_DATABASE_FILE_PATH}" }, "alertsCapabilityAgent":{ - // Path to Alerts database file. e.g. /home/ubuntu/Build/alerts.db - // Note: The directory specified must be valid. - // The database file (alerts.db) will be created by SampleApp, do not create it yourself. - // The database file should only be used for alerts (don't use it for other components of SDK) "databaseFilePath":"${SDK_SQLITE_DATABASE_FILE_PATH}" }, "deviceSettings":{ - // Path to Device Settings database file. e.g. /home/ubuntu/Build/deviceSettings.db - // Note: The directory specified must be valid. - // The database file (deviceSettings.db) will be created by SampleApp, do not create it yourself. - // The database file should only be used for device settings (don't use it for other components of SDK) "databaseFilePath":"${SDK_SQLITE_DEVICE_SETTINGS_DATABASE_FILE_PATH}", - // The list of supported locales on this device. "locales":["en-US","en-GB","de-DE","en-IN","en-CA","ja-JP","en-AU","fr-FR","it-IT","es-ES","es-MX","fr-CA", "es-US", "hi-IN", "pt-BR"], - // The default locale of this device. "defaultLocale":"en-US", - // The list of locale combinations supported on this device. "localeCombinations":[ ["en-US", "es-US"], ["es-US", "en-US"], @@ -64,205 +36,21 @@ ["fr-CA", "en-CA"], ["en-CA", "fr-CA"] ], - // The default timezone of this device. This is an optional parameter; if it isn't specified, Etc/GMT is set as - // the default timezone. "defaultTimezone":"America/Vancouver" }, "bluetooth" : { - // Path to Bluetooth database file. e.g. /home/ubuntu/Build/bluetooth.db - // Note: The directory specified must be valid. - // The database file (bluetooth.db) will be created by SampleApp, do not create it yourself. - // The database file should only be used for bluetooth (don't use it for other components of SDK) "databaseFilePath":"${SDK_BLUETOOTH_DATABASE_FILE_PATH}" }, "certifiedSender":{ - // Path to Certified Sender database file. e.g. /home/ubuntu/Build/certifiedsender.db - // Note: The directory specified must be valid. - // The database file (certifiedsender.db) will be created by SampleApp, do not create it yourself. - // The database file should only be used for certifiedSender (don't use it for other components of SDK) "databaseFilePath":"${SDK_CERTIFIED_SENDER_DATABASE_FILE_PATH}" }, "notifications":{ - // Path to Notifications database file. e.g. /home/ubuntu/Build/notifications.db - // Note: The directory specified must be valid. - // The database file (notifications.db) will be created by SampleApp, do not create it yourself. - // The database file should only be used for notifications (don't use it for other components of SDK) "databaseFilePath":"${SDK_NOTIFICATIONS_DATABASE_FILE_PATH}" }, - "diagnostics":{ - // Flag for allowing tracing from SDK start up (By default, it's false) - "protocolTraceFromStartup": false, - // The maximum number of directive/event messages to store. If exceeded, additional messages will not be stored. - "maxTracedMessages":1 - }, "externalMediaPlayer": { - // Agent identifier provided by AlexaMusic. - // Developers creating their own adaptors must use an Agent ID assigned to them by Alexa Music "agentString": "CQCAFYNYDC" }, - "audioPlayer": { - // To specify the number of MediaPlayer instances for AudioPlayer to use, - // a value of '1' will result in limited pre-buffering: It will buffer during introductory TTS, but not - // the next track. - // A value of '2' will allow next track buffering as well. If multiple Play directives are expected to be enqueued, - // at the same time, and the memory is available, a value of 3 or more will allow additional buffering. - // The default is '2'. - // "audioMediaPlayerPoolSize": 1 - }, "sampleApp": { - // To specify if the SampleApp supports display cards. "displayCardsSupported":true - // The firmware version of the device to send in SoftwareInfo event. - // Note: The firmware version should be a positive 32-bit integer in the range [1-2147483647]. - // e.g. "firmwareVersion": 123 - // The default endpoint to connect to. - // See https://developer.amazon.com/docs/alexa/alexa-voice-service/api-overview.html#endpoints for regions and values - // e.g. "endpoint": "https://alexa.na.gateway.devices.a2z.com" - - // Example of specifying suggested latency in seconds when openning PortAudio stream. By default, - // when this paramater isn't specified, SampleApp calls Pa_OpenDefaultStream to use the default value. - // See http://portaudio.com/docs/v19-doxydocs/structPaStreamParameters.html for further explanation - // on this parameter. - //"portAudio":{ - // "suggestedLatency": 0.150 - //} } - - // Example of specifying output format and the audioSink for the gstreamer-based MediaPlayer bundled with the SDK. - // Many platforms will automatically set the output format correctly, but in some cases where the hardware requires - // a specific format and the software stack is not automatically setting it correctly, these parameters can be used - // to manually specify the output format. Supported rate/format/channels values are documented in detail here: - // https://gstreamer.freedesktop.org/documentation/design/mediatype-audio-raw.html - // - // By default the "autoaudiosink" element is used in the pipeline. This element automatically selects the best sink - // to use based on the configuration in the system. But sometimes the wrong sink is selected and that prevented sound - // from being played. A new configuration is added where the audio sink can be specified for their system. - // "gstreamerMediaPlayer":{ - // "outputConversion":{ - // "rate":16000, - // "format":"S16LE", - // "channels":1 - // }, - // "audioSink":"autoaudiosink" - // }, - - // Example of specifiying curl options that is different from the default values used by libcurl. - // "libcurlUtils":{ - // - // By default libcurl is built with paths to a CA bundle and a directory containing CA certificates. You can - // direct the AVS Device SDK to configure libcurl to use an additional path to directories containing CA - // certificates via the CURLOPT_CAPATH setting. Additional details of this curl option can be found in: - // https://curl.haxx.se/libcurl/c/CURLOPT_CAPATH.html - // "CURLOPT_CAPATH":"INSERT_YOUR_CA_CERTIFICATE_PATH_HERE", - // - // You can specify the AVS Device SDK to use a specific outgoing network interface. More information of - // this curl option can be found here: - // https://curl.haxx.se/libcurl/c/CURLOPT_INTERFACE.html - // "CURLOPT_INTERFACE":"INSERT_YOUR_INTERFACE_HERE" - // }, - - // Example of specifying a default log level for all ModuleLoggers. If not specified, ModuleLoggers get - // their log level from the sink logger. - // "logging":{ - // "logLevel":"INFO" - // }, - - // Example of overriding a specific ModuleLogger's log level whether it was specified by the default value - // provided by the logging.logLevel value (as in the above example) or the log level of the sink logger. - // "acl":{ - // "logLevel":"DEBUG9" - // }, - - // // Example for specifiying the Template Runtime display card timeout values. - // "templateRuntimeCapabilityAgent": { - // // If present, shall override the default timeout for clearing the RenderTemplate display card when SpeechSynthesizer is in FINISHED state. - // "displayCardTTSFinishedTimeout": 2000, - // // If present, shall override the default timeout in ms for clearing the RenderPlayerInfo display card when AudioPlayer is in FINISHED state. - // "displayCardAudioPlaybackFinishedTimeout": 2000, - // // If present, shall override the default timeout in ms for clearing the RenderPlayerInfo display card when AudioPlayer is in STOPPED or PAUSED state. - // "displayCardAudioPlaybackStoppedPausedTimeout": 60000 - // } - - // // The equalizer function allows you to adjust equalizer settings, such as decibel (dB) levels and modes. - // // By default, the equalizer is enabled. The default settings are: - // // * `"enabled":true`. By default, the equalizer is active. - // // * All `"bands"` are active: `BASS`, `MIDRANGE`, `TREBLE`. - // // * "modes" are disabled. See below for more information. - // // * Minimum band level (`"minLevel"`): -6 dB - // // * Maximum band level (`"maxLevel"`): +6 dB - // // * Default state (defaultState): All "bands" are set to 0dB, and no "mode" is active. - - // "equalizer": { - // // Enables or disables the equalizer. Setting this value to `false` will disable the equalizer locally, and report to AVS that it is disabled. - // "enabled": true, - // // The equalizer bands supported by the device. Currently, there are only three available options: `BASS`, `MIDRANGE` and `TREBLE`. - // // By default, all bands are enabled. However, if you specify a band or bands, then only those will be supported. - // // bands will be supported. - // "bands": { - // "BASS": true, - // "MIDRANGE": true, - // "TREBLE": true - // }, - // // The equalizer modes supported by the device. AVS doesn't define specific behavior for modes, - // // the `EqualizerModeControllerInterface` defines this behavior. AVS provides the following options for modes: "MOVIE", "MUSIC", "NIGHT", - // // "SPORT", "TV". By default, all modes are disabled (`false`), unless specifically marked as enabled (`true`). - // "modes": { - // "NIGHT": false, - // "MOVIE": false, - // "MUSIC": false, - // "SPORT": false, - // "TV": false - // }, - // // The equalizer factory settings. These default values are used for a newly registered device, or when a user requests that Alexa reset a band. - // "defaultState": { - // // The default mode to be applied. When no default mode is desired, set the `"mode"` value to `"NONE"`, which is a custom value. - // "mode": "NONE", - // // Defines band level defaults (integer dB) - // "bands": { - // "BASS": 0, - // "MIDRANGE": 0, - // "TREBLE": 0 - // } - // }, - // // Minimum value an equalizer band could have (integer dB). - // "minLevel": -6, - // // Maximum value an equalizer band could have (integer dB). - // "maxLevel": 6, - // // Default delta value to adjust the equalizer band (integer dB). - // "defaultDelta": 1 - // } - - // Example of setting the minUnmuteVolume level in SpeakerManager - // "speakerManagerCapabilityAgent": { - // // If present, shall override the default minUnmuteVolume value that the device restores to when unmuting - // // at volume level 0. - // // "minUnmuteVolume": 10 - // } - - } - - -// Notes for logging -// The log levels are supported to debug when SampleApp is not working as expected. -// There are 14 levels of logging with DEBUG9 providing the highest level of logging and CRITICAL providing -// the lowest level of logging i.e. if DEBUG9 is specified while running the SampleApp, all the logs at DEBUG9 and -// below are displayed, whereas if CRITICAL is specified, only logs of CRITICAL are displayed. -// The 14 levels are: -// DEBUG9, DEBUG8, DEBUG7, DEBUG6, DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, DEBUG0, INFO, WARN, ERROR, CRITICAL. - -// To selectively see the logging for a particular module, you can specify logging level in this json file. -// Some examples are: -// To only see logs of level INFO and below for ACL and MediaPlayer modules, -// - grep for ACSDK_LOG_MODULE in source folder. Find the log module for ACL and MediaPlayer. -// - Put the following in json: - -// "acl":{ -// "logLevel":"INFO" -// }, -// "mediaPlayer":{ -// "logLevel":"INFO" -// } - -// To enable DEBUG, build with cmake option -DCMAKE_BUILD_TYPE=DEBUG. By default it is built with RELEASE build. -// And run the SampleApp similar to the following command. -// e.g. ./SampleApp /home/ubuntu/.../AlexaClientSDKConfig.json /home/ubuntu/KittAiModels/ DEBUG9" +} diff --git a/Integration/CMakeLists.txt b/Integration/CMakeLists.txt index 8435030046..78d41e5378 100644 --- a/Integration/CMakeLists.txt +++ b/Integration/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(Integration LANGUAGES CXX) -include(../build/BuildDefaults.cmake) +include(${AVS_CMAKE_BUILD}/BuildDefaults.cmake) add_subdirectory("src") add_subdirectory("test") diff --git a/Integration/include/Integration/TestMediaPlayer.h b/Integration/include/Integration/TestMediaPlayer.h index 9177b4cc1d..e445af4ff4 100644 --- a/Integration/include/Integration/TestMediaPlayer.h +++ b/Integration/include/Integration/TestMediaPlayer.h @@ -51,6 +51,13 @@ class TestMediaPlayer : public avsCommon::utils::mediaPlayer::MediaPlayerInterfa const avsCommon::utils::mediaPlayer::SourceConfig& config = avsCommon::utils::mediaPlayer::emptySourceConfig()) override; + avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId setSource( + std::shared_ptr attachmentReader, + std::chrono::milliseconds offsetAdjustment, + const avsCommon::utils::AudioFormat* audioFormat = nullptr, + const avsCommon::utils::mediaPlayer::SourceConfig& config = + avsCommon::utils::mediaPlayer::emptySourceConfig()) override; + avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId setSource( std::shared_ptr stream, bool repeat, diff --git a/Integration/src/TestExceptionEncounteredSender.cpp b/Integration/src/TestExceptionEncounteredSender.cpp index 990c40384a..e0b558d809 100644 --- a/Integration/src/TestExceptionEncounteredSender.cpp +++ b/Integration/src/TestExceptionEncounteredSender.cpp @@ -95,7 +95,9 @@ TestExceptionEncounteredSender::ExceptionParams TestExceptionEncounteredSender:: return ret; } -TestExceptionEncounteredSender::ExceptionParams::ExceptionParams() : type{Type::UNSET} { +TestExceptionEncounteredSender::ExceptionParams::ExceptionParams() : + type{Type::UNSET}, + exceptionError{avsCommon::avs::ExceptionErrorType::INTERNAL_ERROR} { } } // namespace test diff --git a/Integration/src/TestMediaPlayer.cpp b/Integration/src/TestMediaPlayer.cpp index cfe64101e4..67521e02d5 100644 --- a/Integration/src/TestMediaPlayer.cpp +++ b/Integration/src/TestMediaPlayer.cpp @@ -49,6 +49,15 @@ avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId TestMediaPlayer::s return ++g_sourceId; } +avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId TestMediaPlayer::setSource( + std::shared_ptr attachmentReader, + std::chrono::milliseconds offsetAdjustment, + const avsCommon::utils::AudioFormat* audioFormat, + const avsCommon::utils::mediaPlayer::SourceConfig& config) { + m_attachmentReader = std::move(attachmentReader); + return ++g_sourceId; +} + avsCommon::utils::mediaPlayer::MediaPlayerInterface::SourceId TestMediaPlayer::setSource( std::shared_ptr stream, bool repeat, diff --git a/Integration/test/AlertsIntegrationTest.cpp b/Integration/test/AlertsIntegrationTest.cpp index cbe24cdb13..b5c6aa76d0 100644 --- a/Integration/test/AlertsIntegrationTest.cpp +++ b/Integration/test/AlertsIntegrationTest.cpp @@ -37,6 +37,7 @@ #include