From 6840059a1023ff97a841c08c2f32b2f15036bd48 Mon Sep 17 00:00:00 2001 From: Michael Woo Date: Mon, 26 Oct 2020 17:14:11 -0700 Subject: [PATCH] Version 1.21 alexa-client-sdk Changes in this update: Feature enhancements, updates, and resolved issues from all releases are available on the [Amazon developer portal](https://developer.amazon.com/docs/alexa/avs-device-sdk/release-notes.html) --- ACL/include/ACL/AVSConnectionManager.h | 51 +- .../ACL/Transport/DownchannelHandler.h | 8 +- ACL/include/ACL/Transport/HTTP2Transport.h | 23 +- .../ACL/Transport/HTTP2TransportFactory.h | 19 +- .../ACL/Transport/MessageRequestHandler.h | 16 +- ACL/include/ACL/Transport/MessageRouter.h | 37 +- .../ACL/Transport/MessageRouterFactory.h | 2 +- .../Transport/MessageRouterFactoryInterface.h | 2 +- .../MessageRouterObserverInterface.h | 5 +- ACL/include/ACL/Transport/MimeResponseSink.h | 6 +- ACL/include/ACL/Transport/PingHandler.h | 19 +- .../ACL/Transport/PostConnectSequencer.h | 6 +- .../Transport/PostConnectSequencerFactory.h | 25 +- .../ACL/Transport/TransportFactoryInterface.h | 4 +- ACL/src/AVSConnectionManager.cpp | 24 +- ACL/src/CMakeLists.txt | 7 +- ACL/src/Transport/DownchannelHandler.cpp | 13 +- ACL/src/Transport/HTTP2Transport.cpp | 62 +- ACL/src/Transport/HTTP2TransportFactory.cpp | 28 +- ACL/src/Transport/MessageRequestHandler.cpp | 25 +- ACL/src/Transport/MessageRouter.cpp | 36 +- ACL/src/Transport/MessageRouterFactory.cpp | 2 +- ACL/src/Transport/MimeResponseSink.cpp | 6 +- ACL/src/Transport/PingHandler.cpp | 25 +- ACL/src/Transport/PostConnectSequencer.cpp | 22 +- .../Transport/PostConnectSequencerFactory.cpp | 73 +- .../Transport/MessageRequestHandlerTest.cpp | 2 +- ACL/test/Transport/MessageRouterTest.h | 7 +- .../Transport/MockMessageRouterObserver.h | 13 +- ADSL/include/ADSL/DirectiveProcessor.h | 7 +- ADSL/include/ADSL/DirectiveSequencer.h | 6 +- ADSL/src/DirectiveProcessor.cpp | 26 +- ADSL/src/DirectiveSequencer.cpp | 24 +- AFML/include/AFML/AudioActivityTracker.h | 24 + AFML/include/AFML/Channel.h | 5 + AFML/include/AFML/FocusManagementComponent.h | 58 + AFML/include/AFML/FocusManager.h | 6 +- AFML/include/AFML/VisualActivityTracker.h | 5 + AFML/src/AudioActivityTracker.cpp | 29 + AFML/src/CMakeLists.txt | 5 +- AFML/src/Channel.cpp | 18 + AFML/src/FocusManagementComponent.cpp | 71 + AFML/src/FocusManager.cpp | 4 +- .../AVS/AbstractAVSConnectionManager.h | 18 +- .../AVS/Attachment/AttachmentManager.h | 8 + .../ActionsToDirectiveMapping.h | 96 + .../CapabilitySemantics/CapabilitySemantics.h | 106 + .../StatesToRangeMapping.h | 105 + .../StatesToValueMapping.h | 121 ++ .../AVSCommon/AVS/DialogUXStateAggregator.h | 41 +- .../AVS/ExceptionEncounteredSender.h | 11 + .../AVS/Initialization/AlexaClientSDKInit.h | 28 + .../Initialization/InitializationParameters.h | 60 + .../InitializationParametersBuilder.h | 84 + .../Initialization/SDKPrimitivesProvider.h | 103 + .../AVS/src/AbstractAVSConnectionManager.cpp | 70 +- AVSCommon/AVS/src/AlexaClientSDKInit.cpp | 81 +- .../AVS/src/Attachment/AttachmentManager.cpp | 4 + .../ActionsToDirectiveMapping.cpp | 124 ++ .../CapabilitySemantics.cpp | 111 ++ .../StatesToRangeMapping.cpp | 126 ++ .../StatesToValueMapping.cpp | 133 ++ AVSCommon/AVS/src/DialogUXStateAggregator.cpp | 101 +- .../AVS/src/ExceptionEncounteredSender.cpp | 6 + .../InitializationParametersBuilder.cpp | 58 + .../Initialization/SDKPrimitivesProvider.cpp | 127 ++ AVSCommon/AVS/test/AlexaClientSDKInitTest.cpp | 6 +- .../AVS/test/CapabilitySemanticsTest.cpp | 443 +++++ .../AVS/test/DialogUXStateAggregatorTest.cpp | 32 + AVSCommon/CMakeLists.txt | 21 +- .../AVSConnectionManagerInterface.h | 6 +- .../AVSGatewayAssignerInterface.h | 7 + .../AlexaEventProcessedObserverInterface.h | 2 + .../ApplicationMediaInterfaces.h | 22 +- .../SDKInterfaces/AudioFocusAnnotation.h | 31 + .../ConnectionStatusObserverInterface.h | 32 + .../DevicePropertyAggregatorInterface.h | 21 + .../Diagnostics/DiagnosticsInterface.h | 5 +- .../Endpoints/DefaultEndpointAnnotation.h | 33 + .../Endpoints/EndpointBuilderInterface.h | 27 +- .../EndpointCapabilitiesBuilderInterface.h | 81 + .../EndpointCapabilitiesRegistrarInterface.h | 81 + .../ExpectSpeechTimeoutHandlerInterface.h | 65 + .../ExternalMediaPlayerInterface.h | 55 - .../HTTPContentFetcherInterface.h | 10 + .../ModeControllerAttributeBuilderInterface.h | 11 + .../ModeController/ModeControllerAttributes.h | 33 +- .../PowerResourceManagerInterface.h | 107 +- ...RangeControllerAttributeBuilderInterface.h | 11 + .../RangeControllerAttributes.h | 32 +- ...layerInfoCardsProviderRegistrarInterface.h | 60 + .../SpeechInteractionHandlerInterface.h | 5 +- .../Timing/TimerDelegateFactoryInterface.h | 55 + .../Timing/TimerDelegateInterface.h | 121 ++ ...oggleControllerAttributeBuilderInterface.h | 11 + .../ToggleControllerAttributes.h | 49 +- .../MockEndpointCapabilitiesRegistrar.h | 57 + .../SDKInterfaces/MockAVSConnectionManager.h | 4 +- .../SDKInterfaces/MockAVSGatewayAssigner.h | 1 + .../SDKInterfaces/MockPowerResourceManager.h | 31 +- AVSCommon/SDKInterfaces/test/CMakeLists.txt | 1 - .../Utils/include/AVSCommon/Utils/Endian.h | 14 + .../AVSCommon/Utils/HTTP/HttpResponseCode.h | 12 + .../LibcurlUtils/CurlEasyHandleWrapper.h | 9 + .../LibcurlUtils/HTTPContentFetcherFactory.h | 18 + .../LibcurlUtils/LibCurlHttpContentFetcher.h | 13 + .../LibcurlHTTP2ConnectionFactory.h | 8 + .../MediaPlayer/MediaPlayerFactoryInterface.h | 6 +- .../MediaPlayerObserverInterface.h | 6 +- .../Utils/MediaPlayer/MediaPlayerState.h | 80 +- .../Utils/MediaPlayer/PlaybackContext.h | 8 + .../MediaPlayer/PooledMediaPlayerFactory.h | 21 +- .../MediaPlayer/PooledMediaResourceProvider.h | 108 + .../PooledMediaResourceProviderInterface.h | 62 + .../Utils/Metrics/UplCalculatorInterface.h | 65 + .../include/AVSCommon/Utils/Metrics/UplData.h | 125 ++ .../Utils/Network/InternetConnectionMonitor.h | 10 + .../Utils/PlaylistParser/PlaylistEntry.h | 3 + .../Power/AggregatedPowerResourceManager.h | 148 ++ .../Utils/Power/NoOpPowerResourceManager.h | 65 + .../AVSCommon/Utils/Power/PowerMonitor.h | 154 ++ .../AVSCommon/Utils/Power/PowerResource.h | 165 ++ .../include/AVSCommon/Utils/Power/WakeGuard.h | 53 + .../include/AVSCommon/Utils/SDKVersion.h | 6 +- .../Threading/ConditionVariableWrapper.h | 197 ++ .../AVSCommon/Utils/Threading/Executor.h | 10 + .../AVSCommon/Utils/Timing/DistantFuture.h | 44 + .../include/AVSCommon/Utils/Timing/Timer.h | 114 +- .../AVSCommon/Utils/Timing/TimerDelegate.h | 100 + .../Utils/Timing/TimerDelegateFactory.h | 41 + .../Utils/UUIDGeneration/UUIDGeneration.h | 19 + .../Utils/include/AVSCommon/Utils/WaitEvent.h | 4 +- .../Utils/include/AVSCommon/Utils/WavUtils.h | 136 ++ AVSCommon/Utils/src/Executor.cpp | 17 +- .../LibcurlUtils/CurlEasyHandleWrapper.cpp | 22 + .../HTTPContentFetcherFactory.cpp | 5 + .../LibCurlHttpContentFetcher.cpp | 16 + .../LibcurlHTTP2ConnectionFactory.cpp | 5 + AVSCommon/Utils/src/Logger/Logger.cpp | 4 +- .../Utils/src/MediaPlayer/PlaybackContext.cpp | 13 +- .../MediaPlayer/PooledMediaPlayerFactory.cpp | 4 +- .../PooledMediaResourceProvider.cpp | 117 ++ AVSCommon/Utils/src/Metrics/UplData.cpp | 106 + .../src/Network/InternetConnectionMonitor.cpp | 12 +- .../Power/AggregatedPowerResourceManager.cpp | 233 +++ AVSCommon/Utils/src/Power/PowerMonitor.cpp | 205 ++ AVSCommon/Utils/src/Power/PowerResource.cpp | 194 ++ AVSCommon/Utils/src/Power/WakeGuard.cpp | 38 + AVSCommon/Utils/src/SystemClockMonitor.cpp | 2 +- .../Threading/ConditionVariableWrapper.cpp | 206 ++ AVSCommon/Utils/src/Timer.cpp | 44 +- AVSCommon/Utils/src/Timing/TimerDelegate.cpp | 142 ++ .../Utils/src/Timing/TimerDelegateFactory.cpp | 36 + AVSCommon/Utils/src/UUIDGeneration.cpp | 95 +- AVSCommon/Utils/src/WaitEvent.cpp | 4 +- AVSCommon/Utils/src/WavUtils.cpp | 294 +++ .../Utils/MediaPlayer/PlaybackContextTest.cpp | 61 +- .../AggregatedPowerResourceManagerTest.cpp | 328 +++ .../Utils/Power/PowerResourceTest.cpp | 323 +++ .../AVSCommon/Utils/Power/WakeGuardTest.cpp | 78 + .../ConditionVariableWrapperTest.cpp | 1019 ++++++++++ AVSCommon/Utils/test/EndianTest.cpp | 44 + AVSCommon/Utils/test/ExecutorTest.cpp | 38 +- AVSCommon/Utils/test/JSONUtilTest.cpp | 9 +- AVSCommon/Utils/test/MIMEParserTest.cpp | 233 +++ AVSCommon/Utils/test/UUIDGenerationTest.cpp | 21 +- .../AVSGatewayManager/AVSGatewayManager.h | 19 + .../Storage/AVSGatewayManagerStorage.h | 9 + AVSGatewayManager/src/AVSGatewayManager.cpp | 28 + AVSGatewayManager/src/CMakeLists.txt | 5 +- .../src/Storage/AVSGatewayManagerStorage.cpp | 11 +- .../AndroidUtilities/PlatformSpecificValues.h | 35 + .../src/AndroidSLESMicrophone.cpp | 3 + .../include/DefaultClient/DefaultClient.h | 355 +++- .../DefaultClient/DefaultClientComponent.h | 135 ++ .../DeviceSettingsManagerBuilder.h | 6 +- .../DefaultClient/EqualizerRuntimeSetup.h | 118 +- .../ExternalCapabilitiesBuilderInterface.h | 11 +- .../StubApplicationAudioPipelineFactory.h | 151 ++ .../DefaultClient/src/CMakeLists.txt | 12 +- .../DefaultClient/src/DefaultClient.cpp | 917 +++++---- .../src/DefaultClientComponent.cpp | 230 +++ .../src/DeviceSettingsManagerBuilder.cpp | 2 +- .../src/EqualizerRuntimeSetup.cpp | 42 +- .../StubApplicationAudioPipelineFactory.cpp | 209 ++ CHANGELOG.md | 21 +- CMakeLists.txt | 3 +- .../CapabilitiesDelegate.h | 41 +- .../DiscoveryEventSender.h | 4 +- .../SQLiteCapabilitiesDelegateStorage.h | 11 + .../Utils/DiscoveryUtils.h | 9 + CapabilitiesDelegate/src/CMakeLists.txt | 9 +- .../src/CapabilitiesDelegate.cpp | 124 +- .../src/DiscoveryEventSender.cpp | 53 +- .../SQLiteCapabilitiesDelegateStorage.cpp | 10 + .../test/CapabilitiesDelegateTest.cpp | 78 +- .../test/DiscoveryEventSenderTest.cpp | 114 +- .../AIP/include/AIP/AudioInputProcessor.h | 39 +- .../AIP/src/AudioInputProcessor.cpp | 39 +- .../AIP/test/AudioInputProcessorTest.cpp | 137 ++ .../Alexa/AlexaEventProcessedNotifier.h | 36 + .../Alexa/AlexaInterfaceCapabilityAgent.h | 81 +- .../Alexa/AlexaInterfaceMessageSender.h | 28 +- .../src/AlexaInterfaceCapabilityAgent.cpp | 111 +- .../Alexa/src/AlexaInterfaceMessageSender.cpp | 28 +- CapabilityAgents/Alexa/src/CMakeLists.txt | 4 + .../AlexaInterfaceCapabilityAgentTest.cpp | 35 +- CapabilityAgents/CMakeLists.txt | 1 - CapabilityAgents/Equalizer/src/CMakeLists.txt | 12 - .../Equalizer/test/CMakeLists.txt | 6 - .../ExternalMediaPlayer/src/CMakeLists.txt | 23 - .../ModeControllerAttributeBuilder.h | 6 + .../src/ModeControllerAttributeBuilder.cpp | 18 +- .../src/ModeControllerCapabilityAgent.cpp | 6 + .../PlaybackController/PlaybackController.h | 20 + .../PlaybackControllerComponent.h | 59 + .../PlaybackController/PlaybackRouter.h | 13 + .../PlaybackController/src/CMakeLists.txt | 6 +- .../src/PlaybackController.cpp | 30 + .../src/PlaybackControllerComponent.cpp | 36 + .../PlaybackController/src/PlaybackRouter.cpp | 17 + .../RangeControllerAttributeBuilder.h | 6 + .../src/RangeControllerAttributeBuilder.cpp | 17 +- .../src/RangeControllerCapabilityAgent.cpp | 7 + .../DefaultChannelVolumeFactory.h | 8 + .../include/SpeakerManager/SpeakerManager.h | 29 + .../SpeakerManager/SpeakerManagerComponent.h | 63 + .../SpeakerManager/src/CMakeLists.txt | 3 +- .../src/DefaultChannelVolumeFactory.cpp | 5 + .../SpeakerManager/src/SpeakerManager.cpp | 53 +- .../src/SpeakerManagerComponent.cpp | 35 + .../SpeechSynthesizer/SpeechSynthesizer.h | 33 + .../SpeechSynthesizer/src/CMakeLists.txt | 3 +- .../src/SpeechSynthesizer.cpp | 40 +- .../SpeechSynthesizer/test/CMakeLists.txt | 2 +- .../test/SpeechSynthesizerTest.cpp | 90 +- .../System/src/ReportStateHandler.cpp | 1 - .../System/test/SoftwareInfoTest.cpp | 7 +- .../RenderPlayerInfoCardsProviderRegistrar.h | 68 + .../include/TemplateRuntime/TemplateRuntime.h | 15 + .../TemplateRuntime/src/CMakeLists.txt | 3 +- ...RenderPlayerInfoCardsProviderRegistrar.cpp | 69 + .../TemplateRuntime/src/TemplateRuntime.cpp | 17 + ...erPlayerInfoCardsProviderRegistrarTest.cpp | 95 + .../test/TemplateRuntimeTest.cpp | 54 +- .../ToggleControllerAttributeBuilder.h | 6 + .../src/ToggleControllerAttributeBuilder.cpp | 17 +- .../src/ToggleControllerCapabilityAgent.cpp | 13 +- Captions/CMakeLists.txt | 1 + Captions/Component/CMakeLists.txt | 1 + .../include/Captions/CaptionsComponent.h | 47 + Captions/Component/src/CMakeLists.txt | 29 + Captions/Component/src/CaptionsComponent.cpp | 77 + .../include/Captions/CaptionManager.h | 5 + .../Implementation/src/CaptionManager.cpp | 71 + .../test/CaptionManagerTest.cpp | 80 +- .../Implementation/test/MockCaptionManager.h | 3 + .../Captions/CaptionManagerInterface.h | 25 + .../include/CertifiedSender/CertifiedSender.h | 11 +- .../CertifiedSender/SQLiteMessageStorage.h | 9 + CertifiedSender/src/CertifiedSender.cpp | 45 +- CertifiedSender/src/SQLiteMessageStorage.cpp | 5 + CertifiedSender/test/CertifiedSenderTest.cpp | 4 +- ContextManager/src/ContextManager.cpp | 1 - .../Diagnostics/DevicePropertyAggregator.h | 31 +- .../include/Diagnostics/DiagnosticsUtils.h | 23 +- Diagnostics/src/CMakeLists.txt | 2 + Diagnostics/src/DevicePropertyAggregator.cpp | 138 +- Diagnostics/src/DiagnosticsUtils.cpp | 56 +- Diagnostics/src/FileBasedAudioInjector.cpp | 10 +- Diagnostics/test/CMakeLists.txt | 3 +- .../test/DevicePropertyAggregatorTest.cpp | 103 + .../Endpoints/DefaultEndpointBuilder.h | 168 ++ .../Endpoints/EndpointAttributeValidation.h | 1 + Endpoints/include/Endpoints/EndpointBuilder.h | 100 +- Endpoints/src/CMakeLists.txt | 3 +- Endpoints/src/DefaultEndpointBuilder.cpp | 230 +++ Endpoints/src/EndpointBuilder.cpp | 157 +- EqualizerImplementations/src/CMakeLists.txt | 19 - EqualizerImplementations/test/CMakeLists.txt | 9 - Integration/AlexaClientSDKConfig.json | 29 +- .../AudioInputProcessorIntegrationTest.cpp | 2 +- .../test/AudioPlayerIntegrationTest.cpp | 9 +- Integration/test/CMakeLists.txt | 1 + .../test/SpeechSynthesizerIntegrationTest.cpp | 2 +- .../src/InterruptModelConfiguration.cpp | 78 + .../include/InterruptModel/InterruptModel.h | 10 + InterruptModel/src/InterruptModel.cpp | 11 + .../AndroidSLESMediaPlayer.h | 10 +- .../src/AndroidSLESMediaPlayer.cpp | 23 +- .../AndroidSLESMediaPlayer/src/CMakeLists.txt | 2 +- .../src/FFmpegDecoder.cpp | 2 +- .../include/MediaPlayer/MediaPlayer.h | 6 +- .../GStreamerMediaPlayer/src/MediaPlayer.cpp | 9 +- .../test/MediaPlayerTest.cpp | 4 + Metrics/CMakeLists.txt | 1 + Metrics/UplCalculator/CMakeLists.txt | 6 + .../include/Metrics/BaseUplCalculator.h | 134 ++ .../include/Metrics/MediaUplCalculator.h | 149 ++ .../include/Metrics/TtsUplCalculator.h | 104 + .../include/Metrics/UplMetricSink.h | 74 + .../UplCalculator/src/BaseUplCalculator.cpp | 174 ++ Metrics/UplCalculator/src/CMakeLists.txt | 14 + .../UplCalculator/src/MediaUplCalculator.cpp | 309 +++ .../UplCalculator/src/TtsUplCalculator.cpp | 188 ++ Metrics/UplCalculator/src/UplMetricSink.cpp | 90 + .../include/PlaylistParser/ContentDecrypter.h | 30 +- .../include/PlaylistParser/PlaylistUtils.h | 5 +- PlaylistParser/src/CMakeLists.txt | 2 +- PlaylistParser/src/ContentDecrypter.cpp | 218 +- .../src/IterativePlaylistParser.cpp | 4 +- PlaylistParser/src/M3UParser.cpp | 9 + PlaylistParser/src/PlaylistParser.cpp | 6 +- PlaylistParser/src/PlaylistUtils.cpp | 15 +- .../src/UrlContentToAttachmentConverter.cpp | 10 +- PlaylistParser/test/ContentDecrypterTest.cpp | 22 + .../test/PlaylistParser/MockContentFetcher.h | 4 + .../CBLAuthDelegate/src/CBLAuthDelegate.cpp | 14 +- SampleApp/CMakeLists.txt | 14 +- .../include/SampleApp/CaptionPresenter.h | 12 + .../include/SampleApp/ConnectionObserver.h | 88 - .../SampleApp/ExternalCapabilitiesBuilder.h | 7 +- .../SampleApp/PlatformSpecificValues.h | 46 + SampleApp/include/SampleApp/SDKDiagnostics.h | 3 +- .../include/SampleApp/SampleApplication.h | 24 +- .../SampleApp/SampleApplicationComponent.h | 31 +- .../SampleApp/SampleEqualizerModeController.h | 15 +- SampleApp/include/SampleApp/UIManager.h | 11 +- SampleApp/src/CMakeLists.txt | 103 +- SampleApp/src/CaptionPresenter.cpp | 11 + SampleApp/src/ConnectionObserver.cpp | 56 - SampleApp/src/ExternalCapabilitiesBuilder.cpp | 16 +- SampleApp/src/GuiRenderer.cpp | 78 +- SampleApp/src/InteractionManager.cpp | 8 - SampleApp/src/SDKDiagnostics.cpp | 3 +- SampleApp/src/SampleApplication.cpp | 269 ++- SampleApp/src/SampleApplicationComponent.cpp | 57 +- .../src/SampleEqualizerModeController.cpp | 7 +- SampleApp/src/UIManager.cpp | 19 +- SampleApp/src/UserInputManager.cpp | 11 +- SampleApp/src/main.cpp | 4 +- .../include/SQLiteStorage/SQLiteMiscStorage.h | 11 + .../SQLiteStorage/src/SQLiteMiscStorage.cpp | 9 + .../PostConnectSynchronizeStateSender.h | 4 +- .../SynchronizeStateSenderFactory.h | 15 + SynchronizeStateSender/src/CMakeLists.txt | 4 +- .../src/PostConnectSynchronizeStateSender.cpp | 10 +- .../src/SynchronizeStateSenderFactory.cpp | 21 + .../MultipartParser/MultipartParser.h | 52 +- .../include/rapidjson/internal/biginteger.h | 2 +- .../include/rapidjson/rapidjson.h | 9 +- .../include/rapidjson/schema.h | 4 +- .../test/unittest/allocatorstest.cpp | 26 +- applications/CMakeLists.txt | 19 +- .../CMakeLists.txt | 4 + .../AndroidApplicationAudioPipelineFactory.h | 172 ++ ...AndroidApplicationAudioPipelineFactory.cpp | 190 ++ .../src/CMakeLists.txt | 22 + .../CMakeLists.txt | 17 + ...ApplicationAudioPipelineFactoryInterface.h | 90 + .../PooledApplicationMediaInterfaces.h | 52 + .../test/CMakeLists.txt | 10 + .../MockApplicationAudioPipelineFactory.h | 58 + .../CMakeLists.txt | 4 + .../AuthorizationDelegateComponent.h | 54 + .../src/AuthorizationDelegateComponent.cpp | 35 + .../src/CMakeLists.txt | 14 + .../CMakeLists.txt | 4 + .../CustomApplicationAudioPipelineFactory.h | 176 ++ .../src/CMakeLists.txt | 20 + .../CustomApplicationAudioPipelineFactory.cpp | 172 ++ .../CMakeLists.txt | 4 + .../InternetConnectionMonitorComponent.h | 45 + .../src/CMakeLists.txt | 13 + .../InternetConnectionMonitorComponent.cpp | 34 + ...DefaultSampleApplicationOptionsComponent.h | 42 +- .../src/CMakeLists.txt | 3 + ...faultSampleApplicationOptionsComponent.cpp | 79 +- .../CMakeLists.txt | 4 + ...GstreamerApplicationAudioPipelineFactory.h | 158 ++ .../src/CMakeLists.txt | 20 + ...treamerApplicationAudioPipelineFactory.cpp | 177 ++ .../CMakeLists.txt | 4 + .../AlexaCommunicationsComponent.h | 54 + .../src/AlexaCommunicationsComponent.cpp | 49 + .../src/CMakeLists.txt | 18 + .../CMakeLists.txt | 4 + .../HTTPContentFetcherComponent.h | 44 + .../src/CMakeLists.txt | 13 + .../src/HTTPContentFetcherComponent.cpp | 34 + .../acsdkNullMetricRecorder/CMakeLists.txt | 4 + .../MetricRecorderComponent.h | 43 + .../src/CMakeLists.txt | 12 + .../src/MetricRecorderComponent.cpp | 31 + .../acsdkPreviewAlexaClient/CMakeLists.txt | 20 + .../acsdkPreviewAlexaClient/README.md | 13 + .../PreviewAlexaClient.h | 239 +++ .../PreviewAlexaClientComponent.h | 123 ++ .../src/CMakeLists.txt | 43 + .../src/PreviewAlexaClient.cpp | 1757 +++++++++++++++++ .../src/PreviewAlexaClientComponent.cpp | 196 ++ .../src/previewMain.cpp | 150 ++ .../CMakeLists.txt | 5 + .../SampleApplicationCBLAuthRequester.h | 66 + .../src/CMakeLists.txt | 13 + .../src/SampleApplicationCBLAuthRequester.cpp | 56 + .../CMakeLists.txt | 12 + .../UIManagerInterface.h | 42 + build/BuildDefaults.cmake | 18 +- build/cmake/Android.cmake | 2 +- build/cmake/BuildOptions.cmake | 16 +- build/cmake/DefaultLibNames.cmake | 18 + build/cmake/EndpointControllers.cmake | 4 + build/cmake/ExternalMediaPlayerAdapters.cmake | 65 +- build/cmake/LocalDucking.cmake | 10 + build/cmake/LowPowerMode.cmake | 11 + build/cmake/MediaPlayer.cmake | 3 +- build/cmake/PortAudio.cmake | 4 + build/cmake/PrepareInstall.cmake | 17 + build/cmake/Rpath.cmake | 19 + .../acsdkAlerts/include/acsdkAlerts/Alert.h | 13 +- .../include/acsdkAlerts/AlertScheduler.h | 46 +- .../acsdkAlerts/AlertsCapabilityAgent.h | 53 +- .../Storage/AlertStorageInterface.h | 27 + .../acsdkAlerts/Storage/SQLiteAlertStorage.h | 30 +- capabilities/Alerts/acsdkAlerts/src/Alert.cpp | 64 +- .../Alerts/acsdkAlerts/src/AlertScheduler.cpp | 101 +- .../acsdkAlerts/src/AlertsCapabilityAgent.cpp | 293 ++- .../src/Storage/SQLiteAlertStorage.cpp | 284 ++- .../acsdkAlerts/test/AlertSchedulerTest.cpp | 65 +- .../Alerts/acsdkAlerts/test/AlertTest.cpp | 4 +- .../test/AlertsCapabilityAgentTest.cpp | 18 + .../include/acsdkAudioPlayer/AudioPlayer.h | 131 +- .../acsdkAudioPlayer/AudioPlayerComponent.h | 106 + .../acsdkAudioPlayer/src/AudioPlayer.cpp | 384 +++- .../src/AudioPlayerComponent.cpp | 160 ++ .../acsdkAudioPlayer/src/CMakeLists.txt | 4 + .../acsdkAudioPlayer/test/AudioPlayerTest.cpp | 313 ++- .../acsdkAudioPlayer/test/CMakeLists.txt | 2 +- .../AudioPlayerObserverInterface.h | 27 + .../acsdkBluetooth/src/Bluetooth.cpp | 28 +- .../src/DoNotDisturbCapabilityAgent.cpp | 1 + capabilities/Equalizer/.clang-format | 102 + capabilities/Equalizer/.gitignore | 1 + capabilities/Equalizer/CMakeLists.txt | 5 + .../Equalizer/acsdkEqualizer}/CMakeLists.txt | 4 +- .../EqualizerCapabilityAgent.h | 38 +- .../acsdkEqualizer/src/CMakeLists.txt | 16 + .../src/EqualizerCapabilityAgent.cpp | 14 +- .../acsdkEqualizer/test/CMakeLists.txt | 6 + .../CMakeLists.txt | 7 + .../EqualizerBandMapperInterface.h | 14 +- .../EqualizerComponent.h | 56 + .../EqualizerController.h | 79 +- .../EqualizerLinearBandMapper.h | 14 +- .../EqualizerUtils.h | 18 +- .../InMemoryEqualizerConfiguration.h | 46 +- .../MiscDBEqualizerStorage.h | 28 +- .../SDKConfigEqualizerConfiguration.h | 30 +- .../src/CMakeLists.txt | 23 + .../src/EqualizerComponent.cpp | 45 + .../src/EqualizerController.cpp | 15 +- .../src/EqualizerLinearBandMapper.cpp | 10 +- .../src/EqualizerUtils.cpp | 8 +- .../src/InMemoryEqualizerConfiguration.cpp | 8 +- .../src/MiscDBEqualizerStorage.cpp | 21 +- .../src/SDKConfigEqualizerConfiguration.cpp | 19 +- .../test/CMakeLists.txt | 14 + .../test/EqualizerControllerTest.cpp | 24 +- .../test/EqualizerLinearBandMapperTest.cpp | 12 +- .../InMemoryEqualizerConfigurationTest.cpp | 9 +- .../test/MiscDBEqualizerStorageTest.cpp | 12 +- .../SDKConfigEqualizerConfigurationTest.cpp | 9 +- .../acsdkEqualizerInterfaces/CMakeLists.txt | 15 + .../EqualizerConfigurationInterface.h | 14 +- .../EqualizerControllerListenerInterface.h | 14 +- .../EqualizerInterface.h | 14 +- .../EqualizerModeControllerInterface.h | 14 +- .../EqualizerRuntimeSetupInterface.h | 105 + .../EqualizerStorageInterface.h | 14 +- .../EqualizerTypes.h | 42 +- .../test/CMakeLists.txt | 8 + .../EqualizerStorageInterfaceTest.h | 18 +- .../MockEqualizerConfigurationInterface.h | 16 +- ...MockEqualizerControllerListenerInterface.h | 16 +- .../MockEqualizerInterface.h | 16 +- .../MockEqualizerModeControllerInterface.h | 18 +- .../MockEqualizerStorageInterface.h | 16 +- .../src/EqualizerStorageInterfaceTest.cpp | 14 +- .../ExternalMediaPlayer/.clang-format | 102 + .../ExternalMediaPlayer/CMakeLists.txt | 4 + .../acsdkExternalMediaPlayer}/CMakeLists.txt | 2 +- .../AuthorizedSender.h | 22 +- .../ExternalMediaAdapterHandler.h | 302 +++ .../ExternalMediaPlayer.h | 515 +++-- .../ExternalMediaPlayerComponent.h | 116 ++ .../StaticExternalMediaPlayerAdapterHandler.h | 101 + .../src/AuthorizedSender.cpp | 11 +- .../src/CMakeLists.txt | 23 + .../src/ExternalMediaAdapterHandler.cpp | 377 ++++ .../src/ExternalMediaPlayer.cpp | 1199 ++++++----- .../src/ExternalMediaPlayerComponent.cpp | 162 ++ ...taticExternalMediaPlayerAdapterHandler.cpp | 213 ++ .../test/CMakeLists.txt | 2 +- .../test/ExternalMediaAdapterHandlerTest.cpp | 268 +++ .../test/ExternalMediaPlayerTest.cpp | 1241 +++++++++--- .../CMakeLists.txt | 4 + .../AdapterUtils.h | 57 +- .../ExternalMediaAdapterConstants.h | 16 +- .../ExternalMediaAdapterHandlerInterface.h | 202 ++ .../ExternalMediaAdapterInterface.h | 140 +- .../ExternalMediaPlayerCommon.h | 104 + .../ExternalMediaPlayerInterface.h | 105 + .../ExternalMediaPlayerObserverInterface.h | 49 +- .../src}/AdapterUtils.cpp | 22 +- .../src/CMakeLists.txt | 14 + .../acsdkMultiRoomMusic/MRMCapabilityAgent.h | 20 +- .../acsdkMultiRoomMusic/MRMHandlerInterface.h | 13 +- .../src/MRMCapabilityAgent.cpp | 12 + .../acsdkNotifications/NotificationRenderer.h | 19 + .../acsdkNotifications/src/CMakeLists.txt | 2 +- .../src/NotificationRenderer.cpp | 26 + .../src/NotificationsCapabilityAgent.cpp | 20 +- .../acsdkNotifications/test/CMakeLists.txt | 3 +- .../test/NotificationRendererTest.cpp | 46 +- core/CMakeLists.txt | 5 +- .../CMakeLists.txt | 15 + .../AlexaEventProcessedNotifierInterface.h | 34 + .../include/acsdkCore/CoreComponent.h | 61 +- core/acsdkCore/src/CMakeLists.txt | 8 +- core/acsdkCore/src/CoreComponent.cpp | 78 +- .../CMakeLists.txt | 3 +- .../PostConnectOperationProviderRegistrar.h | 82 + .../src/CMakeLists.txt | 15 + .../PostConnectOperationProviderRegistrar.cpp | 87 + .../test/CMakeLists.txt | 3 + ...tConnectOperationProviderRegistrarTest.cpp | 158 ++ .../CMakeLists.txt | 14 + ...nnectOperationProviderRegistrarInterface.h | 63 + shared/CMakeLists.txt | 6 + .../acsdkManufactory/ConstructorAdapter.h | 36 +- .../internal/ComponentAccumulator_imp.h | 28 +- .../acsdkManufactory/internal/Component_imp.h | 5 +- .../acsdkManufactory/internal/CookBook.h | 24 +- .../acsdkManufactory/internal/CookBook_imp.h | 112 +- .../internal/Manufactory_imp.h | 13 +- .../internal/RuntimeManufactory_imp.h | 6 +- .../acsdkManufactory/internal/TypeIndex.h | 26 +- shared/acsdkManufactory/src/CookBook.cpp | 25 +- shared/acsdkNotifier/CMakeLists.txt | 16 + .../include/acsdkNotifier/Notifier.h | 146 ++ shared/acsdkNotifier/test/CMakeLists.txt | 3 + shared/acsdkNotifier/test/NotifierTest.cpp | 193 ++ shared/acsdkNotifierInterfaces/CMakeLists.txt | 12 + .../NotifierInterface.h | 72 + shared/acsdkShared/CMakeLists.txt | 1 + .../include/acsdkShared/SharedComponent.h | 22 +- shared/acsdkShared/src/CMakeLists.txt | 5 +- shared/acsdkShared/src/SharedComponent.cpp | 21 +- shared/acsdkShutdownManager/CMakeLists.txt | 6 + .../acsdkShutdownManager/ShutdownManager.h | 63 + .../acsdkShutdownManager/ShutdownNotifier.h | 33 + .../acsdkShutdownManager/src/CMakeLists.txt | 14 + .../src/ShutdownManager.cpp | 64 + .../acsdkShutdownManager/test/CMakeLists.txt | 3 + .../test/ShutdownManagerTest.cpp | 113 ++ .../CMakeLists.txt | 14 + .../ShutdownManagerInterface.h | 48 + .../ShutdownNotifierInterface.h | 35 + shared/acsdkStartupManager/CMakeLists.txt | 6 + .../acsdkStartupManager/StartupManager.h | 60 + .../acsdkStartupManager/StartupNotifier.h | 34 + shared/acsdkStartupManager/src/CMakeLists.txt | 14 + .../src/StartupManager.cpp | 69 + .../acsdkStartupManager/test/CMakeLists.txt | 3 + .../test/StartupManagerTest.cpp | 133 ++ .../CMakeLists.txt | 14 + .../RequiresStartupInterface.h | 43 + .../StartupManagerInterface.h | 47 + .../StartupNotifierInterface.h | 36 + tools/Install/android.sh | 72 +- tools/Install/mingw.sh | 8 + tools/Install/pi.sh | 7 + tools/Install/setup.sh | 2 + tools/Testing.cmake | 1 + 585 files changed, 30385 insertions(+), 4199 deletions(-) create mode 100644 AFML/include/AFML/FocusManagementComponent.h create mode 100644 AFML/src/FocusManagementComponent.cpp create mode 100644 AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/ActionsToDirectiveMapping.h create mode 100644 AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/CapabilitySemantics.h create mode 100644 AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/StatesToRangeMapping.h create mode 100644 AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/StatesToValueMapping.h create mode 100644 AVSCommon/AVS/include/AVSCommon/AVS/Initialization/InitializationParameters.h create mode 100644 AVSCommon/AVS/include/AVSCommon/AVS/Initialization/InitializationParametersBuilder.h create mode 100644 AVSCommon/AVS/include/AVSCommon/AVS/Initialization/SDKPrimitivesProvider.h create mode 100644 AVSCommon/AVS/src/CapabilitySemantics/ActionsToDirectiveMapping.cpp create mode 100644 AVSCommon/AVS/src/CapabilitySemantics/CapabilitySemantics.cpp create mode 100644 AVSCommon/AVS/src/CapabilitySemantics/StatesToRangeMapping.cpp create mode 100644 AVSCommon/AVS/src/CapabilitySemantics/StatesToValueMapping.cpp create mode 100644 AVSCommon/AVS/src/Initialization/InitializationParametersBuilder.cpp create mode 100644 AVSCommon/AVS/src/Initialization/SDKPrimitivesProvider.cpp create mode 100644 AVSCommon/AVS/test/CapabilitySemanticsTest.cpp create mode 100644 AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AudioFocusAnnotation.h create mode 100644 AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/DefaultEndpointAnnotation.h create mode 100644 AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/EndpointCapabilitiesBuilderInterface.h create mode 100644 AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/EndpointCapabilitiesRegistrarInterface.h create mode 100644 AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ExpectSpeechTimeoutHandlerInterface.h delete mode 100644 AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ExternalMediaPlayerInterface.h create mode 100644 AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/RenderPlayerInfoCardsProviderRegistrarInterface.h create mode 100644 AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Timing/TimerDelegateFactoryInterface.h create mode 100644 AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Timing/TimerDelegateInterface.h create mode 100644 AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Endpoints/MockEndpointCapabilitiesRegistrar.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PooledMediaResourceProvider.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PooledMediaResourceProviderInterface.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/Metrics/UplCalculatorInterface.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/Metrics/UplData.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/Power/AggregatedPowerResourceManager.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/Power/NoOpPowerResourceManager.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/Power/PowerMonitor.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/Power/PowerResource.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/Power/WakeGuard.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/Threading/ConditionVariableWrapper.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/Timing/DistantFuture.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/Timing/TimerDelegate.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/Timing/TimerDelegateFactory.h create mode 100644 AVSCommon/Utils/include/AVSCommon/Utils/WavUtils.h create mode 100644 AVSCommon/Utils/src/MediaPlayer/PooledMediaResourceProvider.cpp create mode 100644 AVSCommon/Utils/src/Metrics/UplData.cpp create mode 100644 AVSCommon/Utils/src/Power/AggregatedPowerResourceManager.cpp create mode 100644 AVSCommon/Utils/src/Power/PowerMonitor.cpp create mode 100644 AVSCommon/Utils/src/Power/PowerResource.cpp create mode 100644 AVSCommon/Utils/src/Power/WakeGuard.cpp create mode 100644 AVSCommon/Utils/src/Threading/ConditionVariableWrapper.cpp create mode 100644 AVSCommon/Utils/src/Timing/TimerDelegate.cpp create mode 100644 AVSCommon/Utils/src/Timing/TimerDelegateFactory.cpp create mode 100644 AVSCommon/Utils/src/WavUtils.cpp create mode 100644 AVSCommon/Utils/test/AVSCommon/Utils/Power/AggregatedPowerResourceManagerTest.cpp create mode 100644 AVSCommon/Utils/test/AVSCommon/Utils/Power/PowerResourceTest.cpp create mode 100644 AVSCommon/Utils/test/AVSCommon/Utils/Power/WakeGuardTest.cpp create mode 100644 AVSCommon/Utils/test/AVSCommon/Utils/Threading/ConditionVariableWrapperTest.cpp create mode 100644 AVSCommon/Utils/test/EndianTest.cpp create mode 100644 ApplicationUtilities/AndroidUtilities/include/AndroidUtilities/PlatformSpecificValues.h create mode 100644 ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h create mode 100644 ApplicationUtilities/DefaultClient/include/DefaultClient/StubApplicationAudioPipelineFactory.h create mode 100644 ApplicationUtilities/DefaultClient/src/DefaultClientComponent.cpp create mode 100644 ApplicationUtilities/DefaultClient/src/StubApplicationAudioPipelineFactory.cpp create mode 100644 CapabilityAgents/Alexa/include/Alexa/AlexaEventProcessedNotifier.h delete mode 100644 CapabilityAgents/Equalizer/src/CMakeLists.txt delete mode 100644 CapabilityAgents/Equalizer/test/CMakeLists.txt delete mode 100644 CapabilityAgents/ExternalMediaPlayer/src/CMakeLists.txt create mode 100644 CapabilityAgents/PlaybackController/include/PlaybackController/PlaybackControllerComponent.h create mode 100644 CapabilityAgents/PlaybackController/src/PlaybackControllerComponent.cpp create mode 100644 CapabilityAgents/SpeakerManager/include/SpeakerManager/SpeakerManagerComponent.h create mode 100644 CapabilityAgents/SpeakerManager/src/SpeakerManagerComponent.cpp create mode 100644 CapabilityAgents/TemplateRuntime/include/TemplateRuntime/RenderPlayerInfoCardsProviderRegistrar.h create mode 100644 CapabilityAgents/TemplateRuntime/src/RenderPlayerInfoCardsProviderRegistrar.cpp create mode 100644 CapabilityAgents/TemplateRuntime/test/RenderPlayerInfoCardsProviderRegistrarTest.cpp create mode 100644 Captions/Component/CMakeLists.txt create mode 100644 Captions/Component/include/Captions/CaptionsComponent.h create mode 100644 Captions/Component/src/CMakeLists.txt create mode 100644 Captions/Component/src/CaptionsComponent.cpp create mode 100644 Endpoints/include/Endpoints/DefaultEndpointBuilder.h create mode 100644 Endpoints/src/DefaultEndpointBuilder.cpp delete mode 100644 EqualizerImplementations/src/CMakeLists.txt delete mode 100644 EqualizerImplementations/test/CMakeLists.txt create mode 100644 Metrics/UplCalculator/CMakeLists.txt create mode 100644 Metrics/UplCalculator/include/Metrics/BaseUplCalculator.h create mode 100644 Metrics/UplCalculator/include/Metrics/MediaUplCalculator.h create mode 100644 Metrics/UplCalculator/include/Metrics/TtsUplCalculator.h create mode 100644 Metrics/UplCalculator/include/Metrics/UplMetricSink.h create mode 100644 Metrics/UplCalculator/src/BaseUplCalculator.cpp create mode 100644 Metrics/UplCalculator/src/CMakeLists.txt create mode 100644 Metrics/UplCalculator/src/MediaUplCalculator.cpp create mode 100644 Metrics/UplCalculator/src/TtsUplCalculator.cpp create mode 100644 Metrics/UplCalculator/src/UplMetricSink.cpp delete mode 100644 SampleApp/include/SampleApp/ConnectionObserver.h create mode 100644 SampleApp/include/SampleApp/PlatformSpecificValues.h delete mode 100644 SampleApp/src/ConnectionObserver.cpp create mode 100644 applications/acsdkAndroidApplicationAudioPipelineFactory/CMakeLists.txt create mode 100644 applications/acsdkAndroidApplicationAudioPipelineFactory/include/acsdkAndroidApplicationAudioPipelineFactory/AndroidApplicationAudioPipelineFactory.h create mode 100644 applications/acsdkAndroidApplicationAudioPipelineFactory/src/AndroidApplicationAudioPipelineFactory.cpp create mode 100644 applications/acsdkAndroidApplicationAudioPipelineFactory/src/CMakeLists.txt create mode 100644 applications/acsdkApplicationAudioPipelineFactoryInterfaces/CMakeLists.txt create mode 100644 applications/acsdkApplicationAudioPipelineFactoryInterfaces/include/acsdkApplicationAudioPipelineFactoryInterfaces/ApplicationAudioPipelineFactoryInterface.h create mode 100644 applications/acsdkApplicationAudioPipelineFactoryInterfaces/include/acsdkApplicationAudioPipelineFactoryInterfaces/PooledApplicationMediaInterfaces.h create mode 100644 applications/acsdkApplicationAudioPipelineFactoryInterfaces/test/CMakeLists.txt create mode 100644 applications/acsdkApplicationAudioPipelineFactoryInterfaces/test/acsdkApplicationAudioPipelineFactoryInterfaces/MockApplicationAudioPipelineFactory.h create mode 100644 applications/acsdkCBLAuthorizationDelegate/CMakeLists.txt create mode 100644 applications/acsdkCBLAuthorizationDelegate/include/acsdkAuthorizationDelegate/AuthorizationDelegateComponent.h create mode 100644 applications/acsdkCBLAuthorizationDelegate/src/AuthorizationDelegateComponent.cpp create mode 100644 applications/acsdkCBLAuthorizationDelegate/src/CMakeLists.txt create mode 100644 applications/acsdkCustomApplicationAudioPipelineFactory/CMakeLists.txt create mode 100644 applications/acsdkCustomApplicationAudioPipelineFactory/include/acsdkCustomApplicationAudioPipelineFactory/CustomApplicationAudioPipelineFactory.h create mode 100644 applications/acsdkCustomApplicationAudioPipelineFactory/src/CMakeLists.txt create mode 100644 applications/acsdkCustomApplicationAudioPipelineFactory/src/CustomApplicationAudioPipelineFactory.cpp create mode 100644 applications/acsdkDefaultInternetConnectionMonitor/CMakeLists.txt create mode 100644 applications/acsdkDefaultInternetConnectionMonitor/include/acsdkInternetConnectionMonitor/InternetConnectionMonitorComponent.h create mode 100644 applications/acsdkDefaultInternetConnectionMonitor/src/CMakeLists.txt create mode 100644 applications/acsdkDefaultInternetConnectionMonitor/src/InternetConnectionMonitorComponent.cpp create mode 100644 applications/acsdkGstreamerApplicationAudioPipelineFactory/CMakeLists.txt create mode 100644 applications/acsdkGstreamerApplicationAudioPipelineFactory/include/acsdkGstreamerApplicationAudioPipelineFactory/GstreamerApplicationAudioPipelineFactory.h create mode 100644 applications/acsdkGstreamerApplicationAudioPipelineFactory/src/CMakeLists.txt create mode 100644 applications/acsdkGstreamerApplicationAudioPipelineFactory/src/GstreamerApplicationAudioPipelineFactory.cpp create mode 100644 applications/acsdkLibcurlAlexaCommunications/CMakeLists.txt create mode 100644 applications/acsdkLibcurlAlexaCommunications/include/acsdkAlexaCommunications/AlexaCommunicationsComponent.h create mode 100644 applications/acsdkLibcurlAlexaCommunications/src/AlexaCommunicationsComponent.cpp create mode 100644 applications/acsdkLibcurlAlexaCommunications/src/CMakeLists.txt create mode 100644 applications/acsdkLibcurlHTTPContentFetcher/CMakeLists.txt create mode 100644 applications/acsdkLibcurlHTTPContentFetcher/include/acsdkHTTPContentFetcher/HTTPContentFetcherComponent.h create mode 100644 applications/acsdkLibcurlHTTPContentFetcher/src/CMakeLists.txt create mode 100644 applications/acsdkLibcurlHTTPContentFetcher/src/HTTPContentFetcherComponent.cpp create mode 100644 applications/acsdkNullMetricRecorder/CMakeLists.txt create mode 100644 applications/acsdkNullMetricRecorder/include/acsdkMetricRecorder/MetricRecorderComponent.h create mode 100644 applications/acsdkNullMetricRecorder/src/CMakeLists.txt create mode 100644 applications/acsdkNullMetricRecorder/src/MetricRecorderComponent.cpp create mode 100644 applications/acsdkPreviewAlexaClient/CMakeLists.txt create mode 100644 applications/acsdkPreviewAlexaClient/README.md create mode 100644 applications/acsdkPreviewAlexaClient/include/acsdkPreviewAlexaClient/PreviewAlexaClient.h create mode 100644 applications/acsdkPreviewAlexaClient/include/acsdkPreviewAlexaClient/PreviewAlexaClientComponent.h create mode 100644 applications/acsdkPreviewAlexaClient/src/CMakeLists.txt create mode 100644 applications/acsdkPreviewAlexaClient/src/PreviewAlexaClient.cpp create mode 100644 applications/acsdkPreviewAlexaClient/src/PreviewAlexaClientComponent.cpp create mode 100644 applications/acsdkPreviewAlexaClient/src/previewMain.cpp create mode 100644 applications/acsdkSampleApplicationCBLAuthRequester/CMakeLists.txt create mode 100644 applications/acsdkSampleApplicationCBLAuthRequester/include/acsdkSampleApplicationCBLAuthRequester/SampleApplicationCBLAuthRequester.h create mode 100644 applications/acsdkSampleApplicationCBLAuthRequester/src/CMakeLists.txt create mode 100644 applications/acsdkSampleApplicationCBLAuthRequester/src/SampleApplicationCBLAuthRequester.cpp create mode 100644 applications/acsdkSampleApplicationInterfaces/CMakeLists.txt create mode 100644 applications/acsdkSampleApplicationInterfaces/include/acsdkSampleApplicationInterfaces/UIManagerInterface.h create mode 100644 build/cmake/DefaultLibNames.cmake create mode 100644 build/cmake/LocalDucking.cmake create mode 100644 build/cmake/LowPowerMode.cmake create mode 100644 build/cmake/Rpath.cmake create mode 100644 capabilities/AudioPlayer/acsdkAudioPlayer/include/acsdkAudioPlayer/AudioPlayerComponent.h create mode 100644 capabilities/AudioPlayer/acsdkAudioPlayer/src/AudioPlayerComponent.cpp create mode 100644 capabilities/Equalizer/.clang-format create mode 100644 capabilities/Equalizer/.gitignore create mode 100755 capabilities/Equalizer/CMakeLists.txt rename {CapabilityAgents/Equalizer => capabilities/Equalizer/acsdkEqualizer}/CMakeLists.txt (56%) mode change 100644 => 100755 rename {CapabilityAgents/Equalizer/include/Equalizer => capabilities/Equalizer/acsdkEqualizer/include/acsdkEqualizer}/EqualizerCapabilityAgent.h (85%) mode change 100644 => 100755 create mode 100755 capabilities/Equalizer/acsdkEqualizer/src/CMakeLists.txt rename {CapabilityAgents/Equalizer => capabilities/Equalizer/acsdkEqualizer}/src/EqualizerCapabilityAgent.cpp (98%) mode change 100644 => 100755 create mode 100755 capabilities/Equalizer/acsdkEqualizer/test/CMakeLists.txt create mode 100755 capabilities/Equalizer/acsdkEqualizerImplementations/CMakeLists.txt rename {EqualizerImplementations/include/EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations/include/acsdkEqualizerImplementations}/EqualizerBandMapperInterface.h (74%) mode change 100644 => 100755 create mode 100644 capabilities/Equalizer/acsdkEqualizerImplementations/include/acsdkEqualizerImplementations/EqualizerComponent.h rename {EqualizerImplementations/include/EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations/include/acsdkEqualizerImplementations}/EqualizerController.h (67%) mode change 100644 => 100755 rename {EqualizerImplementations/include/EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations/include/acsdkEqualizerImplementations}/EqualizerLinearBandMapper.h (80%) mode change 100644 => 100755 rename {EqualizerImplementations/include/EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations/include/acsdkEqualizerImplementations}/EqualizerUtils.h (64%) mode change 100644 => 100755 rename {EqualizerImplementations/include/EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations/include/acsdkEqualizerImplementations}/InMemoryEqualizerConfiguration.h (70%) mode change 100644 => 100755 rename {EqualizerImplementations/include/EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations/include/acsdkEqualizerImplementations}/MiscDBEqualizerStorage.h (64%) mode change 100644 => 100755 rename {EqualizerImplementations/include/EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations/include/acsdkEqualizerImplementations}/SDKConfigEqualizerConfiguration.h (70%) create mode 100755 capabilities/Equalizer/acsdkEqualizerImplementations/src/CMakeLists.txt create mode 100644 capabilities/Equalizer/acsdkEqualizerImplementations/src/EqualizerComponent.cpp rename {EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations}/src/EqualizerController.cpp (96%) mode change 100644 => 100755 rename {EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations}/src/EqualizerLinearBandMapper.cpp (96%) mode change 100644 => 100755 rename {EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations}/src/EqualizerUtils.cpp (96%) mode change 100644 => 100755 rename {EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations}/src/InMemoryEqualizerConfiguration.cpp (97%) mode change 100644 => 100755 rename {EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations}/src/MiscDBEqualizerStorage.cpp (90%) rename {EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations}/src/SDKConfigEqualizerConfiguration.cpp (91%) create mode 100755 capabilities/Equalizer/acsdkEqualizerImplementations/test/CMakeLists.txt rename {EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations}/test/EqualizerControllerTest.cpp (96%) mode change 100644 => 100755 rename {EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations}/test/EqualizerLinearBandMapperTest.cpp (97%) mode change 100644 => 100755 rename {EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations}/test/InMemoryEqualizerConfigurationTest.cpp (97%) mode change 100644 => 100755 rename {EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations}/test/MiscDBEqualizerStorageTest.cpp (79%) mode change 100644 => 100755 rename {EqualizerImplementations => capabilities/Equalizer/acsdkEqualizerImplementations}/test/SDKConfigEqualizerConfigurationTest.cpp (97%) mode change 100644 => 100755 create mode 100755 capabilities/Equalizer/acsdkEqualizerInterfaces/CMakeLists.txt rename {AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Audio => capabilities/Equalizer/acsdkEqualizerInterfaces/include/acsdkEqualizerInterfaces}/EqualizerConfigurationInterface.h (85%) mode change 100644 => 100755 rename {AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Audio => capabilities/Equalizer/acsdkEqualizerInterfaces/include/acsdkEqualizerInterfaces}/EqualizerControllerListenerInterface.h (74%) mode change 100644 => 100755 rename {AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Audio => capabilities/Equalizer/acsdkEqualizerInterfaces/include/acsdkEqualizerInterfaces}/EqualizerInterface.h (76%) mode change 100644 => 100755 rename {AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Audio => capabilities/Equalizer/acsdkEqualizerInterfaces/include/acsdkEqualizerInterfaces}/EqualizerModeControllerInterface.h (74%) mode change 100644 => 100755 create mode 100644 capabilities/Equalizer/acsdkEqualizerInterfaces/include/acsdkEqualizerInterfaces/EqualizerRuntimeSetupInterface.h rename {AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Audio => capabilities/Equalizer/acsdkEqualizerInterfaces/include/acsdkEqualizerInterfaces}/EqualizerStorageInterface.h (76%) mode change 100644 => 100755 rename {AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Audio => capabilities/Equalizer/acsdkEqualizerInterfaces/include/acsdkEqualizerInterfaces}/EqualizerTypes.h (78%) mode change 100644 => 100755 create mode 100755 capabilities/Equalizer/acsdkEqualizerInterfaces/test/CMakeLists.txt rename {AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Audio => capabilities/Equalizer/acsdkEqualizerInterfaces/test/include/acsdkEqualizerInterfaces}/EqualizerStorageInterfaceTest.h (75%) mode change 100644 => 100755 rename {AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Audio => capabilities/Equalizer/acsdkEqualizerInterfaces/test/include/acsdkEqualizerInterfaces}/MockEqualizerConfigurationInterface.h (69%) mode change 100644 => 100755 rename {AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Audio => capabilities/Equalizer/acsdkEqualizerInterfaces/test/include/acsdkEqualizerInterfaces}/MockEqualizerControllerListenerInterface.h (62%) mode change 100644 => 100755 rename {AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Audio => capabilities/Equalizer/acsdkEqualizerInterfaces/test/include/acsdkEqualizerInterfaces}/MockEqualizerInterface.h (64%) mode change 100644 => 100755 rename {AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Audio => capabilities/Equalizer/acsdkEqualizerInterfaces/test/include/acsdkEqualizerInterfaces}/MockEqualizerModeControllerInterface.h (55%) mode change 100644 => 100755 rename {AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Audio => capabilities/Equalizer/acsdkEqualizerInterfaces/test/include/acsdkEqualizerInterfaces}/MockEqualizerStorageInterface.h (64%) mode change 100644 => 100755 rename {AVSCommon/SDKInterfaces => capabilities/Equalizer/acsdkEqualizerInterfaces}/test/src/EqualizerStorageInterfaceTest.cpp (88%) mode change 100644 => 100755 create mode 100644 capabilities/ExternalMediaPlayer/.clang-format create mode 100644 capabilities/ExternalMediaPlayer/CMakeLists.txt rename {CapabilityAgents/ExternalMediaPlayer => capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayer}/CMakeLists.txt (75%) rename {CapabilityAgents/ExternalMediaPlayer/include/ExternalMediaPlayer => capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayer/include/acsdkExternalMediaPlayer}/AuthorizedSender.h (75%) create mode 100644 capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayer/include/acsdkExternalMediaPlayer/ExternalMediaAdapterHandler.h rename {CapabilityAgents/ExternalMediaPlayer/include/ExternalMediaPlayer => capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayer/include/acsdkExternalMediaPlayer}/ExternalMediaPlayer.h (51%) create mode 100644 capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayer/include/acsdkExternalMediaPlayer/ExternalMediaPlayerComponent.h create mode 100644 capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayer/include/acsdkExternalMediaPlayer/StaticExternalMediaPlayerAdapterHandler.h rename {CapabilityAgents/ExternalMediaPlayer => capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayer}/src/AuthorizedSender.cpp (94%) create mode 100644 capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayer/src/CMakeLists.txt create mode 100644 capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayer/src/ExternalMediaAdapterHandler.cpp rename {CapabilityAgents/ExternalMediaPlayer => capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayer}/src/ExternalMediaPlayer.cpp (57%) create mode 100644 capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayer/src/ExternalMediaPlayerComponent.cpp create mode 100644 capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayer/src/StaticExternalMediaPlayerAdapterHandler.cpp rename {CapabilityAgents/ExternalMediaPlayer => capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayer}/test/CMakeLists.txt (51%) create mode 100644 capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayer/test/ExternalMediaAdapterHandlerTest.cpp rename {CapabilityAgents/ExternalMediaPlayer => capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayer}/test/ExternalMediaPlayerTest.cpp (65%) create mode 100644 capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayerInterfaces/CMakeLists.txt rename {AVSCommon/AVS/include/AVSCommon/AVS/ExternalMediaPlayer => capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayerInterfaces/include/acsdkExternalMediaPlayerInterfaces}/AdapterUtils.h (71%) rename {AVSCommon/AVS/include/AVSCommon/AVS/ExternalMediaPlayer => capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayerInterfaces/include/acsdkExternalMediaPlayerInterfaces}/ExternalMediaAdapterConstants.h (80%) create mode 100644 capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayerInterfaces/include/acsdkExternalMediaPlayerInterfaces/ExternalMediaAdapterHandlerInterface.h rename {AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces => capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayerInterfaces/include/acsdkExternalMediaPlayerInterfaces}/ExternalMediaAdapterInterface.h (85%) create mode 100644 capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayerInterfaces/include/acsdkExternalMediaPlayerInterfaces/ExternalMediaPlayerCommon.h create mode 100644 capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayerInterfaces/include/acsdkExternalMediaPlayerInterfaces/ExternalMediaPlayerInterface.h rename {AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces => capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayerInterfaces/include/acsdkExternalMediaPlayerInterfaces}/ExternalMediaPlayerObserverInterface.h (78%) rename {AVSCommon/AVS/src/ExternalMediaPlayer => capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayerInterfaces/src}/AdapterUtils.cpp (93%) create mode 100644 capabilities/ExternalMediaPlayer/acsdkExternalMediaPlayerInterfaces/src/CMakeLists.txt mode change 100755 => 100644 capabilities/Notifications/acsdkNotifications/include/acsdkNotifications/NotificationRenderer.h mode change 100755 => 100644 capabilities/Notifications/acsdkNotifications/src/NotificationRenderer.cpp mode change 100755 => 100644 capabilities/Notifications/acsdkNotifications/test/NotificationRendererTest.cpp create mode 100644 core/acsdkAlexaEventProcessedNotifierInterfaces/CMakeLists.txt create mode 100644 core/acsdkAlexaEventProcessedNotifierInterfaces/include/acsdkAlexaEventProcessedNotifierInterfaces/AlexaEventProcessedNotifierInterface.h rename {EqualizerImplementations => core/acsdkPostConnectOperationProviderRegistrar}/CMakeLists.txt (50%) create mode 100644 core/acsdkPostConnectOperationProviderRegistrar/include/acsdkPostConnectOperationProviderRegistrar/PostConnectOperationProviderRegistrar.h create mode 100644 core/acsdkPostConnectOperationProviderRegistrar/src/CMakeLists.txt create mode 100644 core/acsdkPostConnectOperationProviderRegistrar/src/PostConnectOperationProviderRegistrar.cpp create mode 100644 core/acsdkPostConnectOperationProviderRegistrar/test/CMakeLists.txt create mode 100644 core/acsdkPostConnectOperationProviderRegistrar/test/PostConnectOperationProviderRegistrarTest.cpp create mode 100644 core/acsdkPostConnectOperationProviderRegistrarInterfaces/CMakeLists.txt create mode 100644 core/acsdkPostConnectOperationProviderRegistrarInterfaces/include/acsdkPostConnectOperationProviderRegistrarInterfaces/PostConnectOperationProviderRegistrarInterface.h create mode 100644 shared/acsdkNotifier/CMakeLists.txt create mode 100644 shared/acsdkNotifier/include/acsdkNotifier/Notifier.h create mode 100644 shared/acsdkNotifier/test/CMakeLists.txt create mode 100644 shared/acsdkNotifier/test/NotifierTest.cpp create mode 100644 shared/acsdkNotifierInterfaces/CMakeLists.txt create mode 100644 shared/acsdkNotifierInterfaces/include/acsdkNotifierInterfaces/NotifierInterface.h create mode 100644 shared/acsdkShutdownManager/CMakeLists.txt create mode 100644 shared/acsdkShutdownManager/include/acsdkShutdownManager/ShutdownManager.h create mode 100644 shared/acsdkShutdownManager/include/acsdkShutdownManager/ShutdownNotifier.h create mode 100644 shared/acsdkShutdownManager/src/CMakeLists.txt create mode 100644 shared/acsdkShutdownManager/src/ShutdownManager.cpp create mode 100644 shared/acsdkShutdownManager/test/CMakeLists.txt create mode 100644 shared/acsdkShutdownManager/test/ShutdownManagerTest.cpp create mode 100644 shared/acsdkShutdownManagerInterfaces/CMakeLists.txt create mode 100644 shared/acsdkShutdownManagerInterfaces/include/acsdkShutdownManagerInterfaces/ShutdownManagerInterface.h create mode 100644 shared/acsdkShutdownManagerInterfaces/include/acsdkShutdownManagerInterfaces/ShutdownNotifierInterface.h create mode 100644 shared/acsdkStartupManager/CMakeLists.txt create mode 100644 shared/acsdkStartupManager/include/acsdkStartupManager/StartupManager.h create mode 100644 shared/acsdkStartupManager/include/acsdkStartupManager/StartupNotifier.h create mode 100644 shared/acsdkStartupManager/src/CMakeLists.txt create mode 100644 shared/acsdkStartupManager/src/StartupManager.cpp create mode 100644 shared/acsdkStartupManager/test/CMakeLists.txt create mode 100644 shared/acsdkStartupManager/test/StartupManagerTest.cpp create mode 100644 shared/acsdkStartupManagerInterfaces/CMakeLists.txt create mode 100644 shared/acsdkStartupManagerInterfaces/include/acsdkStartupManagerInterfaces/RequiresStartupInterface.h create mode 100644 shared/acsdkStartupManagerInterfaces/include/acsdkStartupManagerInterfaces/StartupManagerInterface.h create mode 100644 shared/acsdkStartupManagerInterfaces/include/acsdkStartupManagerInterfaces/StartupNotifierInterface.h diff --git a/ACL/include/ACL/AVSConnectionManager.h b/ACL/include/ACL/AVSConnectionManager.h index 0a216477a3..a66954ca10 100644 --- a/ACL/include/ACL/AVSConnectionManager.h +++ b/ACL/include/ACL/AVSConnectionManager.h @@ -22,13 +22,12 @@ #include #include +#include #include #include -#include #include #include #include -#include #include #include "ACL/Transport/MessageRouterInterface.h" @@ -69,22 +68,44 @@ namespace acl { */ class AVSConnectionManager : public avsCommon::avs::AbstractAVSConnectionManager - , public avsCommon::sdkInterfaces::MessageSenderInterface - , public avsCommon::sdkInterfaces::AVSGatewayAssignerInterface , public MessageRouterObserverInterface , public avsCommon::sdkInterfaces::InternetConnectionObserverInterface , public avsCommon::utils::RequiresShutdown , public std::enable_shared_from_this { public: + /** + * A factory function that forwards an instance of @c AVSConnectionManagerInterface to a @c MessageSenderInterface. + * + * @param connectionManager The @c MessageSenderInterface implementation to forward. + * @return A shared pointer to a @c MessageSenderInterface. + */ + static std::shared_ptr createMessageSenderInterface( + const std::shared_ptr& connectionManager); + + /** + * A factory function that creates an instance of AVSConnectionManagerInterface. + * + * @param shutdownNotifier The object with which to register to be told when to shut down. + * @param messageRouter The entity which handles sending and receiving of AVS messages. + * @param internetConnectionMonitor The iobject to use to monitor connectivity with the internet. + * @return The created AVSConnectionManager object. + */ + static std::shared_ptr createAVSConnectionManagerInterface( + const std::shared_ptr& shutdownNotifier, + const std::shared_ptr& messageRouter, + const std::shared_ptr& internetConnectionMonitor); + /** * A factory function that creates an AVSConnectionManager object. * + * @deprecated in release 1.21 * @param messageRouter The entity which handles sending and receiving of AVS messages. * @param isEnabled The enablement setting. If true, then the created object will attempt to connect to AVS. * @param connectionStatusObservers An optional set of observers which will be notified when the connection status * changes. The observers cannot be a nullptr. * @param messageObservers An optional set of observer which will be sent messages that arrive from AVS. * The observers cannot be a nullptr. + * @param internetConnectionMonitor The object to use to monitor connectivity with the internet. * @return The created AVSConnectionManager object. */ static std::shared_ptr create( @@ -110,18 +131,16 @@ class AVSConnectionManager void removeMessageObserver(std::shared_ptr observer) override; /// @} + /// @name MessageSenderInterface methods. + /// @{ void sendMessage(std::shared_ptr request) override; + /// @} - /** - * @note Set the gateway URL for the AVS connection. Calling this function with a new value will cause the - * current active connection to be closed, and a new one opened to the new gateway. - */ + /// @name AVSGatewayAssignerInterface methods. + /// @{ void setAVSGateway(const std::string& avsGateway) override; - - /** - * @return The current gateway URL for AVS connection. - */ - std::string getAVSGateway(); + std::string getAVSGateway() const override; + /// @} /// @name InternetConnectionObserverInterface method overrides. /// @{ @@ -136,6 +155,7 @@ class AVSConnectionManager * @param connectionStatusObservers An optional set of observers which will be notified when the connection status * changes. * @param messageObserver An optional observer which will be sent messages that arrive from AVS. + * @param internetConnectionMonitor Interner connection status monitor */ AVSConnectionManager( std::shared_ptr messageRouter, @@ -150,8 +170,9 @@ class AVSConnectionManager void doShutdown() override; void onConnectionStatusChanged( - const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status status, - const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::ChangedReason reason) override; + const ConnectionStatusObserverInterface::Status status, + const std::vector& engineConnectionStatuses) + override; void receive(const std::string& contextId, const std::string& message) override; diff --git a/ACL/include/ACL/Transport/DownchannelHandler.h b/ACL/include/ACL/Transport/DownchannelHandler.h index 9ab4921c09..d650211e2a 100644 --- a/ACL/include/ACL/Transport/DownchannelHandler.h +++ b/ACL/include/ACL/Transport/DownchannelHandler.h @@ -18,8 +18,9 @@ #include -#include +#include #include +#include #include "ACL/Transport/ExchangeHandler.h" #include "ACL/Transport/MessageConsumerInterface.h" @@ -52,7 +53,7 @@ class DownchannelHandler std::shared_ptr context, const std::string& authToken, std::shared_ptr messageConsumer, - std::shared_ptr attachmentManager); + std::shared_ptr attachmentManager); private: /** @@ -76,6 +77,9 @@ class DownchannelHandler void onResponseFinished(avsCommon::utils::http2::HTTP2ResponseFinishedStatus status, const std::string& nonMimeBody) override; /// @} + + /// The power resource to prevent device from going to LPM. + std::shared_ptr m_powerResource; }; } // namespace acl diff --git a/ACL/include/ACL/Transport/HTTP2Transport.h b/ACL/include/ACL/Transport/HTTP2Transport.h index 723a10aab6..95e6c9612a 100644 --- a/ACL/include/ACL/Transport/HTTP2Transport.h +++ b/ACL/include/ACL/Transport/HTTP2Transport.h @@ -18,7 +18,6 @@ #include #include -#include #include #include #include @@ -26,13 +25,16 @@ #include #include -#include +#include #include #include #include #include #include #include +#include +#include +#include #include "ACL/Transport/MessageConsumerInterface.h" #include "ACL/Transport/PingHandler.h" @@ -90,7 +92,7 @@ class HTTP2Transport const std::string& avsGateway, std::shared_ptr http2Connection, std::shared_ptr messageConsumer, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, std::shared_ptr transportObserver, std::shared_ptr postConnectFactory, std::shared_ptr sharedRequestQueue, @@ -223,7 +225,7 @@ class HTTP2Transport const std::string& avsGateway, std::shared_ptr http2Connection, std::shared_ptr messageConsumer, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, std::shared_ptr transportObserver, std::shared_ptr postConnectFactory, std::shared_ptr sharedRequestQueue, @@ -309,8 +311,7 @@ class HTTP2Transport */ State monitorSharedQueueWhileWaiting( State whileState, - std::chrono::time_point maxWakeTime = - std::chrono::time_point::max()); + std::chrono::time_point maxWakeTime = avsCommon::utils::timing::getDistantFuture()); /** * Handle sending @c MessageRequests and pings while in @c State::POST_CONNECTING or @c State::CONNECTED. @@ -377,7 +378,7 @@ class HTTP2Transport std::mutex m_mutex; /// Condition variable use to wake the @c m_thread from various waits. - std::condition_variable m_wakeEvent; + avsCommon::utils::threading::ConditionVariableWrapper m_wakeEvent; /// The current state of this HTTP2Transport. State m_state; @@ -395,7 +396,7 @@ class HTTP2Transport std::shared_ptr m_messageConsumer; /// Object for creating and accessing attachments. - std::shared_ptr m_attachmentManager; + std::shared_ptr m_attachmentManager; /// Factory for creating @c PostConnectInterface instances. std::shared_ptr m_postConnectFactory; @@ -441,6 +442,12 @@ class HTTP2Transport /// The reason for disconnecting. avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::ChangedReason m_disconnectReason; + + /// The mainloop thread power resource. + std::shared_ptr m_mainLoopPowerResource; + + /// The power resource to prevent the device from going into LPM when a messageRequest is active. + std::shared_ptr m_requestActivityPowerResource; }; } // namespace acl diff --git a/ACL/include/ACL/Transport/HTTP2TransportFactory.h b/ACL/include/ACL/Transport/HTTP2TransportFactory.h index a39f652c35..4e0c6d7e25 100644 --- a/ACL/include/ACL/Transport/HTTP2TransportFactory.h +++ b/ACL/include/ACL/Transport/HTTP2TransportFactory.h @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include #include @@ -38,9 +38,24 @@ namespace acl { */ class HTTP2TransportFactory : public TransportFactoryInterface { public: + /** + * Factory to create instances of TransportFactoryInterface. + * + * @param connectionFactory Object used to create instances of HTTP2ConnectionInterface. + * @param postConnectFactory Object used to create instances of the PostConnectInterface. + * @param metricRecorder The metric recorder. + * @param eventTracer Object used to trace events sent to AVS. + */ + static std::shared_ptr createTransportFactoryInterface( + const std::shared_ptr& connectionFactory, + const std::shared_ptr& postConnectFactory, + const std::shared_ptr& metricRecorder, + const std::shared_ptr& eventTracer); + /** * HTTP2TransportFactory constructor. * + * @deprecated * @param connectionFactory Object used to create instances of HTTP2ConnectionInterface. * @param postConnectFactory Object used to create instances of the PostConnectInterface. * @param metricRecorder The metric recorder. @@ -56,7 +71,7 @@ class HTTP2TransportFactory : public TransportFactoryInterface { /// @{ std::shared_ptr createTransport( std::shared_ptr authDelegate, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, const std::string& avsGateway, std::shared_ptr messageConsumerInterface, std::shared_ptr transportObserverInterface, diff --git a/ACL/include/ACL/Transport/MessageRequestHandler.h b/ACL/include/ACL/Transport/MessageRequestHandler.h index 8507146f13..5c6f7dc185 100644 --- a/ACL/include/ACL/Transport/MessageRequestHandler.h +++ b/ACL/include/ACL/Transport/MessageRequestHandler.h @@ -18,9 +18,10 @@ #include -#include +#include #include #include +#include #include #include @@ -55,6 +56,7 @@ class MessageRequestHandler * @param attachmentManager Where to get attachments to write to. * @param metricRecorder The metric recorder. * @param eventTracer The object to trace events sent to AVS. + * @param powerResource The optional @c PowerResource to prevent device from going into LPM. * @return A new MessageRequestHandler or nullptr if the operation fails. */ static std::shared_ptr create( @@ -62,9 +64,10 @@ class MessageRequestHandler const std::string& authToken, std::shared_ptr messageRequest, std::shared_ptr messageConsumer, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, std::shared_ptr metricRecorder, - std::shared_ptr eventTracer = nullptr); + std::shared_ptr eventTracer = nullptr, + const std::shared_ptr& powerResource = nullptr); /// @name HTTP2MimeRequestSourceInterface methods /// @{ @@ -88,12 +91,14 @@ class MessageRequestHandler * @param authToken The token to use to authorize the request. * @param messageRequest The MessageRequest to send. * @param metricRecorder The metric recorder. + * @param powerResource The pointer to @c PowerResource to prevent device from going into LPM. */ MessageRequestHandler( std::shared_ptr context, const std::string& authToken, std::shared_ptr messageRequest, - std::shared_ptr metricRecorder); + std::shared_ptr metricRecorder, + const std::shared_ptr& powerResource); /** * Notify the associated HTTP2Transport instance that the message request failed or was acknowledged by AVS. @@ -134,6 +139,9 @@ class MessageRequestHandler /// Response code received through @c onReceiveResponseCode (or zero). long m_responseCode; + + /// The reference to @c PowerResource to prevent device from going into LPM. + std::shared_ptr m_powerResource; }; } // namespace acl diff --git a/ACL/include/ACL/Transport/MessageRouter.h b/ACL/include/ACL/Transport/MessageRouter.h index 70ee50c592..e35ad68907 100644 --- a/ACL/include/ACL/Transport/MessageRouter.h +++ b/ACL/include/ACL/Transport/MessageRouter.h @@ -21,12 +21,12 @@ #include #include -#include "AVSCommon/Utils/Threading/Executor.h" - -#include +#include +#include #include +#include +#include -#include "AVSCommon/SDKInterfaces/AuthDelegateInterface.h" #include "ACL/Transport/MessageConsumerInterface.h" #include "ACL/Transport/MessageRouterInterface.h" #include "ACL/Transport/MessageRouterObserverInterface.h" @@ -49,8 +49,25 @@ class MessageRouter , public MessageConsumerInterface , public std::enable_shared_from_this { public: + /** + * Factory function for creating an instance of MessageRouterInterface. + * + * @param shutdownNotifier The object with which to register to be told when to shut down. + * @param authDelegate An implementation of an AuthDelegate, which will provide valid access tokens with which + * the MessageRouter can authorize the client to AVS. + * @param attachmentManager The AttachmentManager, which allows ACL to write attachments received from AVS. + * @param transportFactory Factory used to create new transport objects. + */ + static std::shared_ptr createMessageRouterInterface( + const std::shared_ptr& shutdownNotifier, + const std::shared_ptr& authDelegate, + const std::shared_ptr& attachmentManager, + const std::shared_ptr& transportFactory); + /** * Constructor. + * + * @deprecated * @param authDelegate An implementation of an AuthDelegate, which will provide valid access tokens with which * the MessageRouter can authorize the client to AVS. * @param attachmentManager The AttachmentManager, which allows ACL to write attachments received from AVS. @@ -58,12 +75,15 @@ class MessageRouter * @param avsGateway The gateway to connect to AVS. The value will be set by the @c AVSGatewayManager based on * either the previously verified gateway or a value from the config file. If both are not present, a default value * is used. + * @param engineType optional parameter of engine type associated with this MessageRouter. Default to be + * ENGINE_TYPE_ALEXA_VOICE_SERVICES. */ MessageRouter( std::shared_ptr authDelegate, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, std::shared_ptr transportFactory, - const std::string& avsGateway = ""); + const std::string& avsGateway = "", + int engineType = avsCommon::sdkInterfaces::ENGINE_TYPE_ALEXA_VOICE_SERVICES); /// @name MessageRouterInterface methods. /// @{ @@ -184,6 +204,9 @@ class MessageRouter /// The current connection reason. Access serialized with @c m_connectionMutex. avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::ChangedReason m_connectionReason; + /// The engine type associated with this MessageRouter + int m_engineType; + /** * When the MessageRouter is enabled, any disconnect should automatically trigger a reconnect with AVS. * Access serialized with @c m_connectionMutex. @@ -197,7 +220,7 @@ class MessageRouter std::shared_ptr m_activeTransport; /// The attachment manager. - std::shared_ptr m_attachmentManager; + std::shared_ptr m_attachmentManager; /// The transport factory. std::shared_ptr m_transportFactory; diff --git a/ACL/include/ACL/Transport/MessageRouterFactory.h b/ACL/include/ACL/Transport/MessageRouterFactory.h index 8e82b45276..093c46b9ba 100644 --- a/ACL/include/ACL/Transport/MessageRouterFactory.h +++ b/ACL/include/ACL/Transport/MessageRouterFactory.h @@ -40,7 +40,7 @@ class MessageRouterFactory : public MessageRouterFactoryInterface { /// @{ std::shared_ptr createMessageRouter( std::shared_ptr authDelegate, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, std::shared_ptr transportFactory) override; /// @} }; diff --git a/ACL/include/ACL/Transport/MessageRouterFactoryInterface.h b/ACL/include/ACL/Transport/MessageRouterFactoryInterface.h index 1a77a86e9b..7a46a0ede6 100644 --- a/ACL/include/ACL/Transport/MessageRouterFactoryInterface.h +++ b/ACL/include/ACL/Transport/MessageRouterFactoryInterface.h @@ -47,7 +47,7 @@ class MessageRouterFactoryInterface { */ virtual std::shared_ptr createMessageRouter( std::shared_ptr authDelegate, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, std::shared_ptr transportFactory) = 0; }; diff --git a/ACL/include/ACL/Transport/MessageRouterObserverInterface.h b/ACL/include/ACL/Transport/MessageRouterObserverInterface.h index 49a9003f31..220d2d1ea9 100644 --- a/ACL/include/ACL/Transport/MessageRouterObserverInterface.h +++ b/ACL/include/ACL/Transport/MessageRouterObserverInterface.h @@ -39,11 +39,12 @@ class MessageRouterObserverInterface { * This function will be called when the connection status changes. * * @param status The current status of the connection. - * @param reason The reason the connection status changed. + * @param engineStatuses status of observed engine(s) */ virtual void onConnectionStatusChanged( const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status status, - const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::ChangedReason reason) = 0; + const std::vector& + engineStatuses) = 0; /** * This function will be called when a Message arrives from AVS. diff --git a/ACL/include/ACL/Transport/MimeResponseSink.h b/ACL/include/ACL/Transport/MimeResponseSink.h index c367cfd982..6895fad38d 100644 --- a/ACL/include/ACL/Transport/MimeResponseSink.h +++ b/ACL/include/ACL/Transport/MimeResponseSink.h @@ -18,7 +18,7 @@ #include -#include +#include #include #include @@ -47,7 +47,7 @@ class MimeResponseSink : public avsCommon::utils::http2::HTTP2MimeResponseSinkIn MimeResponseSink( std::shared_ptr handler, std::shared_ptr messageConsumer, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, std::string attachmentContextId); /** @@ -93,7 +93,7 @@ class MimeResponseSink : public avsCommon::utils::http2::HTTP2MimeResponseSinkIn std::shared_ptr m_messageConsumer; /// The attachment manager. - std::shared_ptr m_attachmentManager; + std::shared_ptr m_attachmentManager; /// Type of content in the current part. ContentType m_contentType; diff --git a/ACL/include/ACL/Transport/PingHandler.h b/ACL/include/ACL/Transport/PingHandler.h index 8b4b4519a1..c60453488c 100644 --- a/ACL/include/ACL/Transport/PingHandler.h +++ b/ACL/include/ACL/Transport/PingHandler.h @@ -20,6 +20,7 @@ #include #include +#include #include "ACL/Transport/ExchangeHandler.h" @@ -40,11 +41,18 @@ class PingHandler * * @param context The ExchangeContext in which this ping handler will operate. * @param authToken The token to use to authorize the request. + * @param powerResource The optional powerResource object to prevent the device from going into LPM. * @return A new PingHandler or nullptr if the operation fails. */ static std::shared_ptr create( std::shared_ptr context, - const std::string& authToken); + const std::string& authToken, + const std::shared_ptr& powerResource = nullptr); + + /** + * Destructor. + */ + ~PingHandler(); private: /** @@ -52,8 +60,12 @@ class PingHandler * * @param context The ExchangeContext in which this ping handler will operate. * @param authToken The token to use to authorize the request. + * @param powerResource The optional @c PowerResource object to prevent the device from going into LPM. */ - PingHandler(std::shared_ptr context, const std::string& authToken); + PingHandler( + std::shared_ptr context, + const std::string& authToken, + const std::shared_ptr& powerResource); /** * Report that the ping was acknowledged. @@ -79,6 +91,9 @@ class PingHandler /// Response code received through @c onReciveResponseCode (or zero). long m_responseCode; + + /// The power resource to prevent device from going to LPM. + std::shared_ptr m_powerResource; }; } // namespace acl diff --git a/ACL/include/ACL/Transport/PostConnectSequencer.h b/ACL/include/ACL/Transport/PostConnectSequencer.h index ec9058a2ef..b41991db28 100644 --- a/ACL/include/ACL/Transport/PostConnectSequencer.h +++ b/ACL/include/ACL/Transport/PostConnectSequencer.h @@ -16,7 +16,6 @@ #ifndef ALEXA_CLIENT_SDK_ACL_INCLUDE_ACL_TRANSPORT_POSTCONNECTSEQUENCER_H_ #define ALEXA_CLIENT_SDK_ACL_INCLUDE_ACL_TRANSPORT_POSTCONNECTSEQUENCER_H_ -#include #include #include #include @@ -24,11 +23,11 @@ #include #include +#include #include #include "ACL/Transport/PostConnectInterface.h" #include "ACL/Transport/TransportObserverInterface.h" -#include "ACL/Transport/HTTP2Transport.h" namespace alexaClientSDK { namespace acl { @@ -127,6 +126,9 @@ class PostConnectSequencer : public PostConnectInterface { /// Thread upon which @c mainLoop() runs. std::thread m_mainLoopThread; + + /// The mainloop thread power resource. + std::shared_ptr m_mainLoopPowerResource; }; } // namespace acl diff --git a/ACL/include/ACL/Transport/PostConnectSequencerFactory.h b/ACL/include/ACL/Transport/PostConnectSequencerFactory.h index 2c1a46f945..47bf660bd8 100644 --- a/ACL/include/ACL/Transport/PostConnectSequencerFactory.h +++ b/ACL/include/ACL/Transport/PostConnectSequencerFactory.h @@ -20,6 +20,7 @@ #include #include "ACL/Transport/PostConnectFactoryInterface.h" +#include #include namespace alexaClientSDK { @@ -36,6 +37,18 @@ class PostConnectSequencerFactory : public PostConnectFactoryInterface { /** * Creates a new instance of the @c PostConnectSequencer. * + * @param providerRegistrar Registrar from which to get @c PostConnectOperationProviders. + * @return A new instance of the @c PostConnectSequencer. + */ + static std::shared_ptr createPostConnectFactoryInterface( + const std::shared_ptr< + acsdkPostConnectOperationProviderRegistrarInterfaces::PostConnectOperationProviderRegistrarInterface>& + providerRegistrar); + + /** + * Creates a new instance of the @c PostConnectSequencer. + * + * @deprecated * @param postConnectOperationProviders The vector of @c PostConnectOperationProviders. * @return a new instance of the @c PostConnectSequencer. */ @@ -51,13 +64,17 @@ class PostConnectSequencerFactory : public PostConnectFactoryInterface { /** * Constructor. * - * @param postConnectOperationProviders The vector of @c PostConnectOperationProviders. + * @param postConnectOperationProviders The source of post connect providers. */ PostConnectSequencerFactory( - const std::vector>& postConnectOperationProviders); + const std::shared_ptr< + acsdkPostConnectOperationProviderRegistrarInterfaces::PostConnectOperationProviderRegistrarInterface>& + providerRegistrar); - /// The vector of @c PostConnectOperationProviders. - std::vector> m_postConnectOperationProviders; + /// Source of post connect providers + std::shared_ptr< + acsdkPostConnectOperationProviderRegistrarInterfaces::PostConnectOperationProviderRegistrarInterface> + m_registrar; }; } // namespace acl diff --git a/ACL/include/ACL/Transport/TransportFactoryInterface.h b/ACL/include/ACL/Transport/TransportFactoryInterface.h index cc07031ce8..083b7f9818 100644 --- a/ACL/include/ACL/Transport/TransportFactoryInterface.h +++ b/ACL/include/ACL/Transport/TransportFactoryInterface.h @@ -20,7 +20,7 @@ #include #include -#include +#include #include "ACL/Transport/TransportInterface.h" #include "ACL/Transport/MessageConsumerInterface.h" @@ -48,7 +48,7 @@ class TransportFactoryInterface { */ virtual std::shared_ptr createTransport( std::shared_ptr authDelegate, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, const std::string& avsGateway, std::shared_ptr messageConsumerInterface, std::shared_ptr transportObserverInterface, diff --git a/ACL/src/AVSConnectionManager.cpp b/ACL/src/AVSConnectionManager.cpp index 8a2c333033..142a3eef9f 100644 --- a/ACL/src/AVSConnectionManager.cpp +++ b/ACL/src/AVSConnectionManager.cpp @@ -35,6 +35,22 @@ static const std::string TAG("AVSConnectionManager"); */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) +std::shared_ptr AVSConnectionManager::createMessageSenderInterface( + const std::shared_ptr& connectionManager) { + return connectionManager; +} + +std::shared_ptr AVSConnectionManager::createAVSConnectionManagerInterface( + const std::shared_ptr& shutdownNotifier, + const std::shared_ptr& messageRouter, + const std::shared_ptr& internetConnectionMonitor) { + auto avsConnectionManager = create(messageRouter, false, {}, {}, internetConnectionMonitor); + if (avsConnectionManager) { + shutdownNotifier->addObserver(avsConnectionManager); + } + return avsConnectionManager; +} + std::shared_ptr AVSConnectionManager::create( std::shared_ptr messageRouter, bool isEnabled, @@ -154,7 +170,7 @@ void AVSConnectionManager::setAVSGateway(const std::string& avsGateway) { m_messageRouter->setAVSGateway(avsGateway); } -std::string AVSConnectionManager::getAVSGateway() { +std::string AVSConnectionManager::getAVSGateway() const { return m_messageRouter->getAVSGateway(); } @@ -193,8 +209,10 @@ void AVSConnectionManager::removeMessageObserver( void AVSConnectionManager::onConnectionStatusChanged( const ConnectionStatusObserverInterface::Status status, - const ConnectionStatusObserverInterface::ChangedReason reason) { - updateConnectionStatus(status, reason); + const std::vector& + engineConnectionStatuses) { + ACSDK_DEBUG(LX(__func__).d("status", status).d("engine_count", engineConnectionStatuses.size())); + updateConnectionStatus(status, engineConnectionStatuses); } void AVSConnectionManager::receive(const std::string& contextId, const std::string& message) { diff --git a/ACL/src/CMakeLists.txt b/ACL/src/CMakeLists.txt index e4d15e70bc..ee1bbc040f 100644 --- a/ACL/src/CMakeLists.txt +++ b/ACL/src/CMakeLists.txt @@ -6,7 +6,12 @@ target_include_directories(ACL PUBLIC "${MultipartParser_SOURCE_DIR}") target_include_directories(ACL PUBLIC ${CURL_INCLUDE_DIRS}) target_include_directories(ACL PUBLIC "${ACL_SOURCE_DIR}/include") target_include_directories(ACL PUBLIC "${AVSCommon_INCLUDE_DIRS}") -target_link_libraries(ACL ${CURL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} AVSCommon) +target_link_libraries(ACL + ${CURL_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + acsdkPostConnectOperationProviderRegistrarInterfaces + acsdkShutdownManagerInterfaces + AVSCommon) if(NOT MSVC) find_package(Threads ${THREADS_PACKAGE_CONFIG}) diff --git a/ACL/src/Transport/DownchannelHandler.cpp b/ACL/src/Transport/DownchannelHandler.cpp index 799d83c234..263f273d2e 100644 --- a/ACL/src/Transport/DownchannelHandler.cpp +++ b/ACL/src/Transport/DownchannelHandler.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "ACL/Transport/DownchannelHandler.h" #include "ACL/Transport/HTTP2Transport.h" @@ -26,6 +27,7 @@ namespace acl { using namespace avsCommon::utils::http; using namespace avsCommon::utils::http2; +using namespace avsCommon::utils::power; /// Downchannel URL const static std::string AVS_DOWNCHANNEL_URL_PATH_EXTENSION = "/v20160207/directives"; @@ -50,7 +52,7 @@ std::shared_ptr DownchannelHandler::create( std::shared_ptr context, const std::string& authToken, std::shared_ptr messageConsumer, - std::shared_ptr attachmentManager) { + std::shared_ptr attachmentManager) { ACSDK_DEBUG9(LX(__func__).d("context", context.get())); if (!context) { @@ -98,6 +100,10 @@ DownchannelHandler::DownchannelHandler( const std::string& authToken) : ExchangeHandler{context, authToken} { ACSDK_DEBUG9(LX(__func__).d("context", context.get())); + m_powerResource = PowerMonitor::getInstance()->createLocalPowerResource(TAG); + if (m_powerResource) { + m_powerResource->acquire(); + } } void DownchannelHandler::onActivity() { @@ -120,7 +126,9 @@ bool DownchannelHandler::onReceiveResponseCode(long responseCode) { case HTTPResponseCode::REDIRECTION_TEMPORARY_REDIRECT: case HTTPResponseCode::REDIRECTION_PERMANENT_REDIRECT: case HTTPResponseCode::CLIENT_ERROR_BAD_REQUEST: + case HTTPResponseCode::CLIENT_ERROR_THROTTLING_EXCEPTION: case HTTPResponseCode::SERVER_ERROR_INTERNAL: + case HTTPResponseCode::SERVER_UNAVAILABLE: case HTTPResponseCode::SERVER_ERROR_NOT_IMPLEMENTED: break; case HTTPResponseCode::SUCCESS_OK: @@ -130,6 +138,9 @@ bool DownchannelHandler::onReceiveResponseCode(long responseCode) { m_context->onForbidden(m_authToken); break; } + if (m_powerResource) { + m_powerResource->release(); + } return true; } diff --git a/ACL/src/Transport/HTTP2Transport.cpp b/ACL/src/Transport/HTTP2Transport.cpp index 34e6811e11..c96ab241bd 100644 --- a/ACL/src/Transport/HTTP2Transport.cpp +++ b/ACL/src/Transport/HTTP2Transport.cpp @@ -13,16 +13,13 @@ * permissions and limitations under the License. */ -#include #include #include -#include -#include +#include #include -#include #include -#include +#include #include #include "ACL/Transport/DownchannelHandler.h" @@ -38,7 +35,9 @@ using namespace alexaClientSDK::avsCommon::utils; using namespace avsCommon::sdkInterfaces; using namespace avsCommon::avs; using namespace avsCommon::avs::attachment; +using namespace avsCommon::utils::error; using namespace avsCommon::utils::http2; +using namespace avsCommon::utils::power; /// String to identify log entries originating from this file. static const std::string TAG("HTTP2Transport"); @@ -110,7 +109,7 @@ std::shared_ptr HTTP2Transport::create( const std::string& avsGateway, std::shared_ptr http2Connection, std::shared_ptr messageConsumer, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, std::shared_ptr transportObserver, std::shared_ptr postConnectFactory, std::shared_ptr sharedRequestQueue, @@ -183,7 +182,7 @@ HTTP2Transport::HTTP2Transport( const std::string& avsGateway, std::shared_ptr http2Connection, std::shared_ptr messageConsumer, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, std::shared_ptr transportObserver, std::shared_ptr postConnectFactory, std::shared_ptr sharedRequestQueue, @@ -206,6 +205,15 @@ HTTP2Transport::HTTP2Transport( m_configuration{configuration}, m_disconnectReason{ConnectionStatusObserverInterface::ChangedReason::NONE} { m_observers.insert(transportObserver); + + m_mainLoopPowerResource = PowerMonitor::getInstance()->createLocalPowerResource(TAG + "_mainLoop"); + + m_requestActivityPowerResource = + PowerMonitor::getInstance()->createLocalPowerResource(TAG + "_requestActivityResource"); + + if (m_mainLoopPowerResource) { + m_mainLoopPowerResource->acquire(); + } } void HTTP2Transport::addObserver(std::shared_ptr transportObserver) { @@ -274,7 +282,7 @@ bool HTTP2Transport::isConnected() { void HTTP2Transport::onRequestEnqueued() { ACSDK_DEBUG7(LX_P(__func__)); std::lock_guard lock(m_mutex); - m_wakeEvent.notify_all(); + m_wakeEvent.notifyAll(); } void HTTP2Transport::onWakeConnectionRetry() { @@ -295,7 +303,7 @@ void HTTP2Transport::onWakeVerifyConnectivity() { std::lock_guard lock(m_mutex); if (!m_pingHandler) { m_timeOfLastActivity = m_timeOfLastActivity.min(); - m_wakeEvent.notify_all(); + m_wakeEvent.notifyAll(); } } @@ -324,7 +332,7 @@ void HTTP2Transport::sendMessage(std::shared_ptr request) { if (allowed) { m_requestQueue.enqueueRequest(request); - m_wakeEvent.notify_all(); + m_wakeEvent.notifyAll(); } else { ACSDK_ERROR(LX_P("enqueueRequestFailed").d("reason", "notInAllowedState").d("m_state", m_state)); lock.unlock(); @@ -489,14 +497,14 @@ void HTTP2Transport::onMessageRequestAcknowledged(const std::shared_ptrgetIsSerialized()) { m_sharedRequestQueue->clearWaitingForSendAcknowledgement(); } - m_wakeEvent.notify_all(); + m_wakeEvent.notifyAll(); } void HTTP2Transport::onMessageRequestFinished() { std::lock_guard lock(m_mutex); --m_countOfUnfinishedMessageHandlers; ACSDK_DEBUG7(LX_P(__func__).d("countOfUnfinishedMessageHandlers", m_countOfUnfinishedMessageHandlers)); - m_wakeEvent.notify_all(); + m_wakeEvent.notifyAll(); } void HTTP2Transport::onPingRequestAcknowledged(bool success) { @@ -507,7 +515,7 @@ void HTTP2Transport::onPingRequestAcknowledged(bool success) { setStateLocked( State::SERVER_SIDE_DISCONNECT, ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT); } - m_wakeEvent.notify_all(); + m_wakeEvent.notifyAll(); } void HTTP2Transport::onPingTimeout() { @@ -515,7 +523,7 @@ void HTTP2Transport::onPingTimeout() { std::lock_guard lock(m_mutex); m_pingHandler.reset(); setStateLocked(State::SHUTDOWN, ConnectionStatusObserverInterface::ChangedReason::PING_TIMEDOUT); - m_wakeEvent.notify_all(); + m_wakeEvent.notifyAll(); } void HTTP2Transport::onActivity() { @@ -544,6 +552,16 @@ void HTTP2Transport::onGoawayReceived() { void HTTP2Transport::mainLoop() { ACSDK_DEBUG7(LX_P(__func__)); + + PowerMonitor::getInstance()->assignThreadPowerResource(m_mainLoopPowerResource); + + FinallyGuard removePowerResource([this] { + PowerMonitor::getInstance()->removeThreadPowerResource(); + if (m_mainLoopPowerResource) { + m_mainLoopPowerResource->release(); + } + }); + m_http2Connection->addObserver(shared_from_this()); m_postConnect = m_postConnectFactory->createPostConnect(); @@ -677,9 +695,8 @@ HTTP2Transport::State HTTP2Transport::handleDisconnecting() { ACSDK_INFO(LX_P(__func__)); std::unique_lock lock(m_mutex); - while (State::DISCONNECTING == m_state && m_countOfUnfinishedMessageHandlers > 0) { - m_wakeEvent.wait(lock); - } + m_wakeEvent.wait( + lock, [this]() { return (State::DISCONNECTING != m_state) || (m_countOfUnfinishedMessageHandlers == 0); }); setStateLocked(State::SHUTDOWN, ConnectionStatusObserverInterface::ChangedReason::SUCCESS); return m_state; } @@ -736,7 +753,7 @@ HTTP2Transport::State HTTP2Transport::monitorSharedQueueWhileWaiting( auto messageRequestTime = m_sharedRequestQueue->peekRequestTime(); std::unique_lock lock(m_mutex); - m_wakeEvent.wait_until(lock, wakeTime, [this, whileState, messageRequestTime] { + m_wakeEvent.waitUntil(lock, wakeTime, [this, whileState, messageRequestTime] { return m_state != whileState || m_sharedRequestQueue->peekRequestTime() != messageRequestTime; }); @@ -771,7 +788,7 @@ HTTP2Transport::State HTTP2Transport::sendMessagesAndPings( if (m_pingHandler) { m_wakeEvent.wait(lock, pingWakePredicate); } else { - m_wakeEvent.wait_until(lock, m_timeOfLastActivity + m_configuration.inactivityTimeout, wakePredicate); + m_wakeEvent.waitUntil(lock, m_timeOfLastActivity + m_configuration.inactivityTimeout, wakePredicate); } if (m_state != whileState) { @@ -791,7 +808,8 @@ HTTP2Transport::State HTTP2Transport::sendMessagesAndPings( m_messageConsumer, m_attachmentManager, m_metricRecorder, - m_eventTracer); + m_eventTracer, + m_requestActivityPowerResource); if (!handler) { messageRequest->sendCompleted(MessageRequestObserverInterface::Status::INTERNAL_ERROR); } @@ -808,7 +826,7 @@ HTTP2Transport::State HTTP2Transport::sendMessagesAndPings( auto authToken = m_authDelegate->getAuthToken(); if (!authToken.empty()) { - m_pingHandler = PingHandler::create(shared_from_this(), authToken); + m_pingHandler = PingHandler::create(shared_from_this(), authToken, m_requestActivityPowerResource); } else { ACSDK_ERROR(LX_P("failedToCreatePingHandler").d("reason", "invalidAuth")); } @@ -895,7 +913,7 @@ bool HTTP2Transport::setStateLocked(State newState, ConnectionStatusObserverInte } m_state = newState; - m_wakeEvent.notify_all(); + m_wakeEvent.notifyAll(); return true; } diff --git a/ACL/src/Transport/HTTP2TransportFactory.cpp b/ACL/src/Transport/HTTP2TransportFactory.cpp index 26a292eddd..7c95cd4904 100644 --- a/ACL/src/Transport/HTTP2TransportFactory.cpp +++ b/ACL/src/Transport/HTTP2TransportFactory.cpp @@ -26,9 +26,35 @@ using namespace alexaClientSDK::avsCommon::utils; using namespace avsCommon::sdkInterfaces; using namespace avsCommon::avs::attachment; +/// String to identify log entries originating from this file. +static const std::string TAG("HTTP2TransportFactory"); + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param The event string for this @c LogEntry. + */ +#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) + +std::shared_ptr HTTP2TransportFactory::createTransportFactoryInterface( + const std::shared_ptr& connectionFactory, + const std::shared_ptr& postConnectFactory, + const std::shared_ptr& metricRecorder, + const std::shared_ptr& eventTracer) { + if (!connectionFactory) { + ACSDK_ERROR(LX("createTransportFactoryInterfaceFailed").d("reason", "nullConnectionFactory")); + return nullptr; + } + if (!postConnectFactory) { + ACSDK_ERROR(LX("createTransportFactoryInterfaceFailed").d("reason", "nullPostConnectFactory")); + return nullptr; + } + return std::make_shared(connectionFactory, postConnectFactory, metricRecorder, eventTracer); +} + std::shared_ptr HTTP2TransportFactory::createTransport( std::shared_ptr authDelegate, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, const std::string& avsGateway, std::shared_ptr messageConsumerInterface, std::shared_ptr transportObserverInterface, diff --git a/ACL/src/Transport/MessageRequestHandler.cpp b/ACL/src/Transport/MessageRequestHandler.cpp index 90aab62a98..f5d301bebe 100644 --- a/ACL/src/Transport/MessageRequestHandler.cpp +++ b/ACL/src/Transport/MessageRequestHandler.cpp @@ -37,6 +37,7 @@ using namespace avsCommon::sdkInterfaces; using namespace avsCommon::utils::http; using namespace avsCommon::utils::http2; using namespace avsCommon::utils::metrics; +using namespace avsCommon::utils::power; /// URL to send events to const static std::string AVS_EVENT_URL_PATH_EXTENSION = "/v20160207/events"; @@ -118,6 +119,9 @@ static void collectSendDataResultMetric( MessageRequestHandler::~MessageRequestHandler() { reportMessageRequestAcknowledged(); reportMessageRequestFinished(); + if (m_powerResource) { + m_powerResource->release(); + } } std::shared_ptr MessageRequestHandler::create( @@ -125,9 +129,10 @@ std::shared_ptr MessageRequestHandler::create( const std::string& authToken, std::shared_ptr messageRequest, std::shared_ptr messageConsumer, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, std::shared_ptr metricRecorder, - std::shared_ptr eventTracer) { + std::shared_ptr eventTracer, + const std::shared_ptr& powerResource) { ACSDK_DEBUG7(LX(__func__).d("context", context.get()).d("messageRequest", messageRequest.get())); if (!context) { @@ -141,7 +146,7 @@ std::shared_ptr MessageRequestHandler::create( } std::shared_ptr handler( - new MessageRequestHandler(context, authToken, messageRequest, std::move(metricRecorder))); + new MessageRequestHandler(context, authToken, messageRequest, std::move(metricRecorder), powerResource)); // Allow custom path extension, if provided by the sender of the MessageRequest @@ -183,7 +188,8 @@ MessageRequestHandler::MessageRequestHandler( std::shared_ptr context, const std::string& authToken, std::shared_ptr messageRequest, - std::shared_ptr metricRecorder) : + std::shared_ptr metricRecorder, + const std::shared_ptr& powerResource) : ExchangeHandler{context, authToken}, m_messageRequest{messageRequest}, m_json{messageRequest->getJsonContent()}, @@ -193,8 +199,13 @@ MessageRequestHandler::MessageRequestHandler( m_metricRecorder{metricRecorder}, m_wasMessageRequestAcknowledgeReported{false}, m_wasMessageRequestFinishedReported{false}, - m_responseCode{0} { + m_responseCode{0}, + m_powerResource{powerResource} { ACSDK_DEBUG7(LX(__func__).d("context", context.get()).d("messageRequest", messageRequest.get())); + + if (m_powerResource) { + m_powerResource->acquire(); + } } void MessageRequestHandler::reportMessageRequestAcknowledged() { @@ -363,7 +374,9 @@ void MessageRequestHandler::onResponseFinished(HTTP2ResponseFinishedStatus statu {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::SERVER_ERROR_INTERNAL, MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2}}; + {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; diff --git a/ACL/src/Transport/MessageRouter.cpp b/ACL/src/Transport/MessageRouter.cpp index 2589e26a7c..a52f7e3392 100644 --- a/ACL/src/Transport/MessageRouter.cpp +++ b/ACL/src/Transport/MessageRouter.cpp @@ -43,16 +43,44 @@ static constexpr const char* KEY_SIZEOF_TRANSPORTS = "sizeOf m_transports"; */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) +std::shared_ptr MessageRouter::createMessageRouterInterface( + const std::shared_ptr& shutdownNotifier, + const std::shared_ptr& authDelegate, + const std::shared_ptr& attachmentManager, + const std::shared_ptr& transportFactory) { + if (!shutdownNotifier) { + ACSDK_ERROR(LX("createMessageRouterInterfaceFailed").d("reason", "nullShutdownNotifier")); + return nullptr; + } + if (!authDelegate) { + ACSDK_ERROR(LX("createMessageRouterInterfaceFailed").d("reason", "nullAuthDelegate")); + return nullptr; + } + if (!attachmentManager) { + ACSDK_ERROR(LX("createMessageRouterInterfaceFailed").d("reason", "nullAttachmentManager")); + return nullptr; + } + if (!transportFactory) { + ACSDK_ERROR(LX("createMessageRouterInterfaceFailed").d("reason", "nullTransportFactory")); + return nullptr; + } + auto messageRouter = std::make_shared(authDelegate, attachmentManager, transportFactory); + shutdownNotifier->addObserver(messageRouter); + return messageRouter; +} + MessageRouter::MessageRouter( std::shared_ptr authDelegate, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, std::shared_ptr transportFactory, - const std::string& avsGateway) : + const std::string& avsGateway, + int engineType) : MessageRouterInterface{"MessageRouter"}, m_avsGateway{avsGateway}, m_authDelegate{authDelegate}, m_connectionStatus{ConnectionStatusObserverInterface::Status::DISCONNECTED}, m_connectionReason{ConnectionStatusObserverInterface::ChangedReason::ACL_CLIENT_REQUEST}, + m_engineType{engineType}, m_isEnabled{false}, m_attachmentManager{attachmentManager}, m_transportFactory{transportFactory}, @@ -273,7 +301,9 @@ void MessageRouter::notifyObserverOnConnectionStatusChanged( auto task = [this, status, reason]() { auto observer = getObserver(); if (observer) { - observer->onConnectionStatusChanged(status, reason); + std::vector statuses; + statuses.emplace_back(m_engineType, reason, status); + observer->onConnectionStatusChanged(status, statuses); } }; m_executor.submit(task); diff --git a/ACL/src/Transport/MessageRouterFactory.cpp b/ACL/src/Transport/MessageRouterFactory.cpp index 45f8850271..b70d903e41 100644 --- a/ACL/src/Transport/MessageRouterFactory.cpp +++ b/ACL/src/Transport/MessageRouterFactory.cpp @@ -28,7 +28,7 @@ MessageRouterFactory::MessageRouterFactory() = default; std::shared_ptr MessageRouterFactory::createMessageRouter( std::shared_ptr authDelegate, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, std::shared_ptr transportFactory) { return std::make_shared( std::move(authDelegate), std::move(attachmentManager), std::move(transportFactory)); diff --git a/ACL/src/Transport/MimeResponseSink.cpp b/ACL/src/Transport/MimeResponseSink.cpp index ea1199b111..92531a6bd2 100644 --- a/ACL/src/Transport/MimeResponseSink.cpp +++ b/ACL/src/Transport/MimeResponseSink.cpp @@ -23,7 +23,7 @@ namespace acl { using namespace avsCommon::avs::attachment; using namespace avsCommon::utils::http2; -#ifdef DEBUG +#ifdef ACSDK_DEBUG_LOG_ENABLED /// Carriage return static const char CR = 0x0D; #endif @@ -82,7 +82,7 @@ static std::string sanitizeContentId(const std::string& mimeContentId) { MimeResponseSink::MimeResponseSink( std::shared_ptr handler, std::shared_ptr messageConsumer, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, std::string attachmentContextId) : m_handler{handler}, m_messageConsumer{messageConsumer}, @@ -108,7 +108,7 @@ bool MimeResponseSink::onReceiveHeaderLine(const std::string& line) { m_handler->onActivity(); } -#ifdef DEBUG +#ifdef ACSDK_DEBUG_LOG_ENABLED if (0 == line.find(X_AMZN_REQUESTID_PREFIX)) { auto end = line.find(CR); ACSDK_DEBUG0(LX("receivedRequestId").d("value", line.substr(0, end))); diff --git a/ACL/src/Transport/PingHandler.cpp b/ACL/src/Transport/PingHandler.cpp index ed01406d99..0a67eae733 100644 --- a/ACL/src/Transport/PingHandler.cpp +++ b/ACL/src/Transport/PingHandler.cpp @@ -15,6 +15,7 @@ #include #include +#include #include "ACL/Transport/HTTP2Transport.h" #include "ACL/Transport/PingHandler.h" @@ -24,6 +25,7 @@ namespace acl { using namespace avsCommon::utils::http; using namespace avsCommon::utils::http2; +using namespace avsCommon::utils::power; /// URL to send pings to const static std::string AVS_PING_URL_PATH_EXTENSION = "/ping"; @@ -49,7 +51,8 @@ static const std::string TAG("PingHandler"); std::shared_ptr PingHandler::create( std::shared_ptr context, - const std::string& authToken) { + const std::string& authToken, + const std::shared_ptr& powerResource) { ACSDK_DEBUG5(LX(__func__).d("context", context.get())); if (!context) { @@ -62,7 +65,7 @@ std::shared_ptr PingHandler::create( return nullptr; } - std::shared_ptr handler(new PingHandler(context, authToken)); + std::shared_ptr handler(new PingHandler(context, authToken, powerResource)); HTTP2RequestConfig cfg{ HTTP2RequestType::GET, context->getAVSGateway() + AVS_PING_URL_PATH_EXTENSION, PING_ID_PREFIX}; @@ -81,11 +84,25 @@ std::shared_ptr PingHandler::create( return handler; } -PingHandler::PingHandler(std::shared_ptr context, const std::string& authToken) : +PingHandler::PingHandler( + std::shared_ptr context, + const std::string& authToken, + const std::shared_ptr& powerResource) : ExchangeHandler{context, authToken}, m_wasPingAcknowledgedReported{false}, - m_responseCode{0} { + m_responseCode{0}, + m_powerResource{powerResource} { ACSDK_DEBUG5(LX(__func__).d("context", context.get())); + + if (m_powerResource) { + m_powerResource->acquire(); + } +} + +PingHandler::~PingHandler() { + if (m_powerResource) { + m_powerResource->release(); + } } void PingHandler::reportPingAcknowledged() { diff --git a/ACL/src/Transport/PostConnectSequencer.cpp b/ACL/src/Transport/PostConnectSequencer.cpp index 1cccd8779c..18f5b6a7c3 100644 --- a/ACL/src/Transport/PostConnectSequencer.cpp +++ b/ACL/src/Transport/PostConnectSequencer.cpp @@ -13,14 +13,18 @@ * permissions and limitations under the License. */ -#include "ACL/Transport/PostConnectSequencer.h" - +#include #include +#include + +#include "ACL/Transport/PostConnectSequencer.h" namespace alexaClientSDK { namespace acl { using namespace avsCommon::sdkInterfaces; +using namespace avsCommon::utils::error; +using namespace avsCommon::utils::power; /// String to identify log entries originating form this file. static const std::string TAG("PostConnectSequencer"); @@ -46,6 +50,11 @@ std::shared_ptr PostConnectSequencer::create( PostConnectSequencer::PostConnectSequencer(const PostConnectOperationsSet& postConnectOperations) : m_isStopping{false}, m_postConnectOperations{postConnectOperations} { + m_mainLoopPowerResource = PowerMonitor::getInstance()->createLocalPowerResource(TAG + "_mainLoop"); + + if (m_mainLoopPowerResource) { + m_mainLoopPowerResource->acquire(); + } } PostConnectSequencer::~PostConnectSequencer() { @@ -85,6 +94,15 @@ void PostConnectSequencer::mainLoop( std::shared_ptr postConnectObserver) { ACSDK_DEBUG5(LX(__func__)); + PowerMonitor::getInstance()->assignThreadPowerResource(m_mainLoopPowerResource); + + FinallyGuard removePowerResource([this] { + PowerMonitor::getInstance()->removeThreadPowerResource(); + if (m_mainLoopPowerResource) { + m_mainLoopPowerResource->release(); + } + }); + if (!postConnectSender) { ACSDK_ERROR(LX("mainLoopError").d("reason", "nullPostConnectSender")); return; diff --git a/ACL/src/Transport/PostConnectSequencerFactory.cpp b/ACL/src/Transport/PostConnectSequencerFactory.cpp index ba615261e5..46506840d6 100644 --- a/ACL/src/Transport/PostConnectSequencerFactory.cpp +++ b/ACL/src/Transport/PostConnectSequencerFactory.cpp @@ -24,6 +24,8 @@ namespace alexaClientSDK { namespace acl { using namespace avsCommon::sdkInterfaces; +using namespace acsdkPostConnectOperationProviderRegistrarInterfaces; + /// String to identify log entries originating from this file. static const std::string TAG("PostConnectSequencerFactory"); @@ -34,25 +36,84 @@ static const std::string TAG("PostConnectSequencerFactory"); */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) +/** + * An implementation of PostConnectOperationProviderRegistrarInterface that adapts the existing interface + * of PostConnectSequencerFactory that takes a vector of providers to the new PostConnectSequencerFactory + * that takes an instance of PostConnectOperationProviderRegistrarInterface. + */ +class LegacyProviderRegistrar : public PostConnectOperationProviderRegistrarInterface { +public: + /** + * Constructor. + * + * @param postConnectOperationProviders The providers to (pre) register. + */ + LegacyProviderRegistrar( + const std::vector>& postConnectOperationProviders); + + /// @name PostConnectOperationProviderRegistrarInterface methods. + /// @{ + bool registerProvider( + const std::shared_ptr& provider) override; + avsCommon::utils::Optional< + std::vector>> + getProviders() override; + /// @} + +private: + /// The registered providers. + std::vector> m_providers; +}; + +LegacyProviderRegistrar::LegacyProviderRegistrar( + const std::vector>& providers) : + m_providers{providers} { +} + +bool LegacyProviderRegistrar::registerProvider(const std::shared_ptr& provider) { + return false; +} + +avsCommon::utils::Optional< + std::vector>> +LegacyProviderRegistrar::getProviders() { + return m_providers; +} + +std::shared_ptr PostConnectSequencerFactory::createPostConnectFactoryInterface( + const std::shared_ptr& registrar) { + if (!registrar) { + ACSDK_ERROR(LX("createPostConectFactoryInterfaceFailed").d("reason", "nullRegistrar")); + return nullptr; + } + return std::shared_ptr(new PostConnectSequencerFactory(registrar)); +} + std::shared_ptr PostConnectSequencerFactory::create( - const std::vector>& postConnectOperationProviders) { - for (auto& provider : postConnectOperationProviders) { + const std::vector>& providers) { + for (auto& provider : providers) { if (!provider) { ACSDK_ERROR(LX("createFailed").d("reason", "invalidProviderFound")); return nullptr; } } - return std::shared_ptr(new PostConnectSequencerFactory(postConnectOperationProviders)); + auto registrar = std::make_shared(providers); + return std::shared_ptr(new PostConnectSequencerFactory(registrar)); } PostConnectSequencerFactory::PostConnectSequencerFactory( - const std::vector>& postConnectOperationProviders) : - m_postConnectOperationProviders{postConnectOperationProviders} { + const std::shared_ptr& registrar) : + m_registrar{registrar} { } std::shared_ptr PostConnectSequencerFactory::createPostConnect() { + auto providers = m_registrar->getProviders(); + if (!providers.hasValue()) { + ACSDK_ERROR(LX("createPostConnectFailed").d("reason", "nullProviders")); + return nullptr; + } PostConnectSequencer::PostConnectOperationsSet postConnectOperationsSet; - for (auto& provider : m_postConnectOperationProviders) { + for (auto& provider : providers.value()) { auto postConnectOperation = provider->createPostConnectOperation(); if (postConnectOperation) { postConnectOperationsSet.insert(postConnectOperation); diff --git a/ACL/test/Transport/MessageRequestHandlerTest.cpp b/ACL/test/Transport/MessageRequestHandlerTest.cpp index 23ed817a3e..35f5456e9c 100644 --- a/ACL/test/Transport/MessageRequestHandlerTest.cpp +++ b/ACL/test/Transport/MessageRequestHandlerTest.cpp @@ -81,7 +81,7 @@ TEST_F(MessageRequestHandlerTest, test_headers) { AUTH_TOKEN, messageRequest, std::shared_ptr(), - std::shared_ptr(), + std::shared_ptr(), std::shared_ptr()); const std::vector actual = classUnderTest->getRequestHeaderLines(); diff --git a/ACL/test/Transport/MessageRouterTest.h b/ACL/test/Transport/MessageRouterTest.h index 0161ea58e2..6a1cfd5168 100644 --- a/ACL/test/Transport/MessageRouterTest.h +++ b/ACL/test/Transport/MessageRouterTest.h @@ -21,6 +21,7 @@ #include #include +#include "AVSCommon/AVS/Attachment/AttachmentManager.h" #include "AVSCommon/Utils/Threading/Executor.h" #include "AVSCommon/Utils/Memory/Memory.h" @@ -46,7 +47,7 @@ class TestableMessageRouter : public MessageRouter { public: TestableMessageRouter( std::shared_ptr authDelegate, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, std::shared_ptr factory, const std::string& avsGateway) : MessageRouter(authDelegate, attachmentManager, factory, avsGateway) { @@ -77,7 +78,7 @@ class MockTransportFactory : public TransportFactoryInterface { private: std::shared_ptr createTransport( std::shared_ptr authDelegate, - std::shared_ptr attachmentManager, + std::shared_ptr attachmentManager, const std::string& avsGateway, std::shared_ptr messageConsumerInterface, std::shared_ptr transportObserverInterface, @@ -138,7 +139,7 @@ class MessageRouterTest : public ::testing::Test { std::shared_ptr m_mockMessageRouterObserver; std::shared_ptr m_mockAuthDelegate; - std::shared_ptr m_attachmentManager; + std::shared_ptr m_attachmentManager; std::shared_ptr> m_mockTransport; std::shared_ptr m_transportFactory; std::shared_ptr m_router; diff --git a/ACL/test/Transport/MockMessageRouterObserver.h b/ACL/test/Transport/MockMessageRouterObserver.h index 011094d2bb..fa2b71aa61 100644 --- a/ACL/test/Transport/MockMessageRouterObserver.h +++ b/ACL/test/Transport/MockMessageRouterObserver.h @@ -62,11 +62,20 @@ class MockMessageRouterObserver : public MessageRouterObserverInterface { private: virtual void onConnectionStatusChanged( const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status status, - const avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::ChangedReason reason) override { + const std::vector& + engineConnectionStatuses) override { + if (engineConnectionStatuses.empty()) { + return; + } notifiedOfStatusChanged = true; m_status = status; - m_reason = reason; + for (auto connectionStatus : engineConnectionStatuses) { + if (connectionStatus.engineType == avsCommon::sdkInterfaces::ENGINE_TYPE_ALEXA_VOICE_SERVICES) { + m_reason = connectionStatus.reason; + } + } } + virtual void receive(const std::string& contextId, const std::string& message) override { notifiedOfReceive = true; m_attachmentContextId = contextId; diff --git a/ADSL/include/ADSL/DirectiveProcessor.h b/ADSL/include/ADSL/DirectiveProcessor.h index 40f1074bdf..7b97895b77 100644 --- a/ADSL/include/ADSL/DirectiveProcessor.h +++ b/ADSL/include/ADSL/DirectiveProcessor.h @@ -29,6 +29,8 @@ #include #include +#include +#include #include "ADSL/DirectiveRouter.h" @@ -310,7 +312,7 @@ class DirectiveProcessor { std::deque m_handlingQueue; /// Condition variable used to wake @c processingLoop() when it is waiting. - std::condition_variable m_wakeProcessingLoop; + avsCommon::utils::threading::ConditionVariableWrapper m_wakeProcessingLoop; /// Thread processing elements on @c m_handlingQueue and @c m_cancelingQueue. std::thread m_processingThread; @@ -338,6 +340,9 @@ class DirectiveProcessor { */ std::array, avsCommon::avs::BlockingPolicy::Medium::COUNT> m_directivesBeingHandled; + + /// The @c PowerResource associated with this thread. + std::shared_ptr m_powerResource; }; } // namespace adsl diff --git a/ADSL/include/ADSL/DirectiveSequencer.h b/ADSL/include/ADSL/DirectiveSequencer.h index 4ee5051bbc..64bb403185 100644 --- a/ADSL/include/ADSL/DirectiveSequencer.h +++ b/ADSL/include/ADSL/DirectiveSequencer.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "ADSL/DirectiveProcessor.h" #include "ADSL/DirectiveRouter.h" @@ -112,7 +113,10 @@ class DirectiveSequencer : public avsCommon::sdkInterfaces::DirectiveSequencerIn std::deque> m_receivingQueue; /// Condition variable used to wake m_receivingLoop when waiting. - std::condition_variable m_wakeReceivingLoop; + avsCommon::utils::threading::ConditionVariableWrapper m_wakeReceivingLoop; + + /// The @c PowerResource associated with this thread. + std::shared_ptr m_powerResource; /// Thread to receive directives. std::thread m_receivingThread; diff --git a/ADSL/src/DirectiveProcessor.cpp b/ADSL/src/DirectiveProcessor.cpp index 2ecbf4a9b2..3ca3dd2c55 100644 --- a/ADSL/src/DirectiveProcessor.cpp +++ b/ADSL/src/DirectiveProcessor.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "ADSL/DirectiveProcessor.h" @@ -39,6 +40,7 @@ namespace adsl { using namespace avsCommon; using namespace avsCommon::avs; using namespace avsCommon::sdkInterfaces; +using namespace avsCommon::utils; std::mutex DirectiveProcessor::m_handleMapMutex; DirectiveProcessor::ProcessorHandle DirectiveProcessor::m_nextProcessorHandle = 0; @@ -51,6 +53,11 @@ DirectiveProcessor::DirectiveProcessor(DirectiveRouter* directiveRouter) : std::lock_guard lock(m_handleMapMutex); m_handle = ++m_nextProcessorHandle; m_handleMap[m_handle] = this; + m_powerResource = power::PowerMonitor::getInstance()->createLocalPowerResource(TAG); + + if (m_powerResource) { + m_powerResource->acquire(); + } m_processingThread = std::thread(&DirectiveProcessor::processingLoop, this); } @@ -117,7 +124,7 @@ bool DirectiveProcessor::onDirective(std::shared_ptr directive) { auto item = std::make_pair(directive, policy); m_handlingQueue.push_back(item); - m_wakeProcessingLoop.notify_one(); + m_wakeProcessingLoop.notifyOne(); return true; } @@ -131,7 +138,7 @@ void DirectiveProcessor::shutdown() { std::lock_guard lock(m_mutex); queueAllDirectivesForCancellationLocked(); m_isShuttingDown = true; - m_wakeProcessingLoop.notify_one(); + m_wakeProcessingLoop.notifyOne(); } if (m_processingThread.joinable()) { m_processingThread.join(); @@ -143,7 +150,7 @@ void DirectiveProcessor::disable() { ACSDK_DEBUG(LX("disable")); queueAllDirectivesForCancellationLocked(); m_isEnabled = false; - m_wakeProcessingLoop.notify_one(); + m_wakeProcessingLoop.notifyOne(); } bool DirectiveProcessor::enable() { @@ -230,7 +237,7 @@ void DirectiveProcessor::removeDirectiveLocked(std::shared_ptr dir } if (!m_cancelingQueue.empty() || !m_handlingQueue.empty()) { - m_wakeProcessingLoop.notify_one(); + m_wakeProcessingLoop.notifyOne(); } } @@ -320,6 +327,8 @@ std::deque::iterator DirectiveProcessor: void DirectiveProcessor::processingLoop() { ACSDK_DEBUG9(LX("processingLoop")); + power::PowerMonitor::getInstance()->assignThreadPowerResource(m_powerResource); + auto haveUnblockedDirectivesToHandle = [this] { return getNextUnblockedDirectiveLocked() != m_handlingQueue.end(); }; @@ -346,6 +355,11 @@ void DirectiveProcessor::processingLoop() { } while (cancelHandled || queuedHandled); } while (!m_isShuttingDown); + + power::PowerMonitor::getInstance()->removeThreadPowerResource(); + if (m_powerResource) { + m_powerResource->release(); + } } bool DirectiveProcessor::processCancelingQueueLocked(std::unique_lock& lock) { @@ -484,7 +498,7 @@ void DirectiveProcessor::scrubDialogRequestIdLocked(const std::string& dialogReq // If there were any changes, wake up the processing loop. if (changed) { ACSDK_DEBUG9(LX("notifyingProcessingLoop").d("size:", m_cancelingQueue.size())); - m_wakeProcessingLoop.notify_one(); + m_wakeProcessingLoop.notifyOne(); } } @@ -521,7 +535,7 @@ void DirectiveProcessor::queueAllDirectivesForCancellationLocked() { if (changed) { ACSDK_DEBUG9(LX("notifyingProcessingLoop")); - m_wakeProcessingLoop.notify_one(); + m_wakeProcessingLoop.notifyOne(); } } diff --git a/ADSL/src/DirectiveSequencer.cpp b/ADSL/src/DirectiveSequencer.cpp index a9045480d6..af31691fe8 100644 --- a/ADSL/src/DirectiveSequencer.cpp +++ b/ADSL/src/DirectiveSequencer.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include "ADSL/DirectiveSequencer.h" @@ -82,7 +84,7 @@ bool DirectiveSequencer::onDirective(std::shared_ptr directive) { } ACSDK_INFO(LX("onDirective").d("directive", directive->getHeaderAsString())); m_receivingQueue.push_back(directive); - m_wakeReceivingLoop.notify_one(); + m_wakeReceivingLoop.notifyOne(); return true; } @@ -94,7 +96,12 @@ DirectiveSequencer::DirectiveSequencer( m_exceptionSender{exceptionSender}, m_isShuttingDown{false}, m_isEnabled{true}, - m_directiveRouter{metricRecorder} { + m_directiveRouter{metricRecorder}, + m_powerResource{power::PowerMonitor::getInstance()->createLocalPowerResource(TAG)} { + if (m_powerResource) { + m_powerResource->acquire(); + } + m_directiveProcessor = std::make_shared(&m_directiveRouter); m_receivingThread = std::thread(&DirectiveSequencer::receivingLoop, this); } @@ -104,7 +111,7 @@ void DirectiveSequencer::doShutdown() { { std::lock_guard lock(m_mutex); m_isShuttingDown = true; - m_wakeReceivingLoop.notify_one(); + m_wakeReceivingLoop.notifyOne(); } if (m_receivingThread.joinable()) { m_receivingThread.join(); @@ -120,7 +127,7 @@ void DirectiveSequencer::disable() { m_isEnabled = false; m_directiveProcessor->setDialogRequestId(""); m_directiveProcessor->disable(); - m_wakeReceivingLoop.notify_one(); + m_wakeReceivingLoop.notifyOne(); } void DirectiveSequencer::enable() { @@ -128,12 +135,14 @@ void DirectiveSequencer::enable() { std::lock_guard lock(m_mutex); m_isEnabled = true; m_directiveProcessor->enable(); - m_wakeReceivingLoop.notify_one(); + m_wakeReceivingLoop.notifyOne(); } void DirectiveSequencer::receivingLoop() { auto wake = [this]() { return !m_receivingQueue.empty() || m_isShuttingDown; }; + power::PowerMonitor::getInstance()->assignThreadPowerResource(m_powerResource); + std::unique_lock lock(m_mutex); while (true) { m_wakeReceivingLoop.wait(lock, wake); @@ -142,6 +151,11 @@ void DirectiveSequencer::receivingLoop() { } receiveDirectiveLocked(lock); } + + power::PowerMonitor::getInstance()->removeThreadPowerResource(); + if (m_powerResource) { + m_powerResource->release(); + } } void DirectiveSequencer::receiveDirectiveLocked(std::unique_lock& lock) { diff --git a/AFML/include/AFML/AudioActivityTracker.h b/AFML/include/AFML/AudioActivityTracker.h index f72e155979..0036e9cde7 100644 --- a/AFML/include/AFML/AudioActivityTracker.h +++ b/AFML/include/AFML/AudioActivityTracker.h @@ -22,10 +22,15 @@ #include #include +#include +#include #include +#include #include #include #include +#include +#include #include #include @@ -46,6 +51,25 @@ class AudioActivityTracker , public avsCommon::sdkInterfaces::CapabilityConfigurationInterface , public avsCommon::sdkInterfaces::StateProviderInterface { public: + /** + * Creates a new @c ActivityTrackerInterface instance, annotated with @c AudioFocusAnnotation. + * + * @param contextManager The AVS Context manager used to generate system context for events. + * @param shutdownNotifier The object to notify this instance when it is time to shut down. + * @param defaultEndpointCapabilitiesRegistrar The capabilities registrar for the default endpoint with which + * to register this capability. + * @return An @c Annotated @c std::shared_ptr to the new @c AudioActivityTracker instance, or @c nullptr if the + * operation failed. + */ + static acsdkManufactory::Annotated + createAudioActivityTrackerInterface( + std::shared_ptr contextManager, + std::shared_ptr shutdownNotifier, + acsdkManufactory::Annotated< + avsCommon::sdkInterfaces::endpoints::DefaultEndpointAnnotation, + avsCommon::sdkInterfaces::endpoints::EndpointCapabilitiesRegistrarInterface> + defaultEndpointCapabilitiesRegistrar); + /** * Creates a new @c AudioActivityTracker instance. * diff --git a/AFML/include/AFML/Channel.h b/AFML/include/AFML/Channel.h index 1b2d396eba..67b578c20e 100644 --- a/AFML/include/AFML/Channel.h +++ b/AFML/include/AFML/Channel.h @@ -28,6 +28,8 @@ #include #include +#include + namespace alexaClientSDK { namespace afml { @@ -286,6 +288,9 @@ class Channel { /// Activity to track the receiver of patience. std::shared_ptr m_patienceReceiver; + + /// Used to manage power levels. + std::shared_ptr m_powerResource; }; } // namespace afml } // namespace alexaClientSDK diff --git a/AFML/include/AFML/FocusManagementComponent.h b/AFML/include/AFML/FocusManagementComponent.h new file mode 100644 index 0000000000..78fa08fa29 --- /dev/null +++ b/AFML/include/AFML/FocusManagementComponent.h @@ -0,0 +1,58 @@ +/* + * 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_AFML_INCLUDE_AFML_FOCUSMANAGEMENTCOMPONENT_H_ +#define ALEXA_CLIENT_SDK_AFML_INCLUDE_AFML_FOCUSMANAGEMENTCOMPONENT_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace alexaClientSDK { +namespace afml { + +/** + * Definition of a Manufactory component for focus management. + */ +using FocusManagementComponent = acsdkManufactory::Component< + acsdkManufactory:: + Annotated, + acsdkManufactory::Import>, + acsdkManufactory::Import>, + acsdkManufactory::Import>, + acsdkManufactory::Import>>; + +/** + * Creates an manufactory component that exports a shared pointer to @c Annotated @c FocusManagers. + * + * @return A component. + */ +FocusManagementComponent getComponent(); + +} // namespace afml +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AFML_INCLUDE_AFML_FOCUSMANAGEMENTCOMPONENT_H_ diff --git a/AFML/include/AFML/FocusManager.h b/AFML/include/AFML/FocusManager.h index cbddf59838..d813191802 100644 --- a/AFML/include/AFML/FocusManager.h +++ b/AFML/include/AFML/FocusManager.h @@ -24,13 +24,15 @@ #include #include +#include +#include #include #include +#include +#include #include "AFML/Channel.h" #include "AFML/ActivityTrackerInterface.h" -#include "AVSCommon/Utils/Threading/Executor.h" -#include "InterruptModel/InterruptModel.h" namespace alexaClientSDK { namespace afml { diff --git a/AFML/include/AFML/VisualActivityTracker.h b/AFML/include/AFML/VisualActivityTracker.h index a674a08a49..717b8dc7b4 100644 --- a/AFML/include/AFML/VisualActivityTracker.h +++ b/AFML/include/AFML/VisualActivityTracker.h @@ -20,12 +20,17 @@ #include #include +#include +#include #include #include #include #include +#include +#include #include #include +#include #include "AFML/Channel.h" #include "AFML/ActivityTrackerInterface.h" diff --git a/AFML/src/AudioActivityTracker.cpp b/AFML/src/AudioActivityTracker.cpp index b5673ced85..b0ef2fd6ba 100644 --- a/AFML/src/AudioActivityTracker.cpp +++ b/AFML/src/AudioActivityTracker.cpp @@ -28,6 +28,8 @@ namespace alexaClientSDK { namespace afml { +using namespace acsdkManufactory; +using namespace acsdkShutdownManagerInterfaces; using namespace avsCommon::sdkInterfaces; using namespace avsCommon::utils; using namespace avsCommon::avs; @@ -66,6 +68,33 @@ static const char INTERFACE_KEY[] = "interface"; */ static std::shared_ptr getAudioActivityTrackerCapabilityConfiguration(); +Annotated AudioActivityTracker::createAudioActivityTrackerInterface( + std::shared_ptr contextManager, + std::shared_ptr shutdownNotifier, + Annotated + defaultEndpointCapabilitiesRegistrar) { + if (!shutdownNotifier) { + ACSDK_ERROR(LX("createAudioActivityTrackerInterfaceFailed").d("reason", "nullShutdownNotifier")); + return nullptr; + } + if (!defaultEndpointCapabilitiesRegistrar) { + ACSDK_ERROR( + LX("createAudioActivityTrackerInterfaceFailed").d("reason", "nullDefaultEndpointCapabilitiesRegistrar")); + return nullptr; + } + + auto audioActivityTracker = AudioActivityTracker::create(contextManager); + if (!audioActivityTracker) { + ACSDK_ERROR(LX("createAudioActivityTrackerInterfaceFailed")); + return nullptr; + } + + defaultEndpointCapabilitiesRegistrar->withCapabilityConfiguration(audioActivityTracker); + shutdownNotifier->addObserver(audioActivityTracker); + + return Annotated(audioActivityTracker); +} + std::shared_ptr AudioActivityTracker::create( std::shared_ptr contextManager) { if (!contextManager) { diff --git a/AFML/src/CMakeLists.txt b/AFML/src/CMakeLists.txt index abf6c86e27..916797dbe0 100644 --- a/AFML/src/CMakeLists.txt +++ b/AFML/src/CMakeLists.txt @@ -1,12 +1,13 @@ add_library(AFML SHARED AudioActivityTracker.cpp Channel.cpp + FocusManagementComponent.cpp FocusManager.cpp VisualActivityTracker.cpp) add_definitions("-DACSDK_LOG_MODULE=afml") -include_directories(AFML "${AFML_SOURCE_DIR}/include" "{InterruptModel_SOURCE_DIR}/include") -target_link_libraries(AFML AVSCommon InterruptModel) +target_include_directories(AFML PUBLIC "${AFML_SOURCE_DIR}/include" "{InterruptModel_SOURCE_DIR}/include") +target_link_libraries(AFML AVSCommon InterruptModel acsdkManufactory acsdkShutdownManagerInterfaces) # install target asdk_install() diff --git a/AFML/src/Channel.cpp b/AFML/src/Channel.cpp index de0c825746..3158ae00ce 100644 --- a/AFML/src/Channel.cpp +++ b/AFML/src/Channel.cpp @@ -15,12 +15,14 @@ #include "AFML/Channel.h" #include +#include namespace alexaClientSDK { namespace afml { using namespace avsCommon::avs; using namespace avsCommon::sdkInterfaces; +using namespace avsCommon::utils; /// String to identify log entries originating from this file. static const std::string TAG("Channel"); @@ -45,6 +47,12 @@ Channel::Channel(const std::string& name, const unsigned int priority, bool isVi m_priority{priority}, m_isVirtual{isVirtual}, m_state{name} { + /// Non-refcounted. + m_powerResource = power::PowerResource::create( + TAG + ":" + name, + power::PowerMonitor::getInstance()->getPowerResourceManager(), + PowerResourceManagerInterface::PowerResourceLevel::STANDBY_MED, + false); } const std::string& Channel::getName() const { @@ -95,6 +103,10 @@ void Channel::setPrimaryActivity(std::shared_ptracquire(); + } + ACSDK_DEBUG5(LX(__func__).d("Interface", activity->getInterface())); if (!m_activities.empty()) { processPolicyLocked(activity, m_activities.front()); @@ -267,6 +279,12 @@ bool Channel::removeActivityHelperLocked( m_activities.erase(activityToRemoveIt); updateChannelInterfaceLocked(); + if (m_activities.empty()) { + if (m_powerResource) { + m_powerResource->release(); + } + } + return true; } diff --git a/AFML/src/FocusManagementComponent.cpp b/AFML/src/FocusManagementComponent.cpp new file mode 100644 index 0000000000..9627d7b204 --- /dev/null +++ b/AFML/src/FocusManagementComponent.cpp @@ -0,0 +1,71 @@ +/* + * 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 "AFML/AudioActivityTracker.h" +#include "AFML/FocusManagementComponent.h" +#include "AFML/FocusManager.h" + +namespace alexaClientSDK { +namespace afml { + +using AudioFocusAnnotation = avsCommon::sdkInterfaces::AudioFocusAnnotation; + +/// String to identify log entries originating from this file. +static const std::string TAG("FocusManagementComponent"); + +/** + * 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) + +/// Key for audio channel array configurations in configuration node. +static const std::string AUDIO_CHANNEL_CONFIG_KEY = "audioChannels"; + +acsdkManufactory::Annotated +createAudioFocusManager( + acsdkManufactory::Annotated annotatedActivityTracker, + std::shared_ptr interruptModel) { + // Read audioChannels configuration from config file + std::vector audioVirtualChannelConfiguration; + if (!afml::FocusManager::ChannelConfiguration::readChannelConfiguration( + AUDIO_CHANNEL_CONFIG_KEY, &audioVirtualChannelConfiguration)) { + ACSDK_ERROR(LX("initializeFailed").d("reason", "unableToReadAudioChannelConfiguration")); + return nullptr; + } + + std::shared_ptr activityTracker = annotatedActivityTracker; + + auto focusManager = std::make_shared( + afml::FocusManager::getDefaultAudioChannels(), + activityTracker, + audioVirtualChannelConfiguration, + interruptModel); + + return acsdkManufactory::Annotated( + focusManager); +} + +FocusManagementComponent getComponent() { + return acsdkManufactory::ComponentAccumulator<>() + .addRetainedFactory(AudioActivityTracker::createAudioActivityTrackerInterface) + .addRequiredFactory(createAudioFocusManager); +} + +} // namespace afml +} // namespace alexaClientSDK diff --git a/AFML/src/FocusManager.cpp b/AFML/src/FocusManager.cpp index 87ad039848..c7c7b6ec5c 100644 --- a/AFML/src/FocusManager.cpp +++ b/AFML/src/FocusManager.cpp @@ -15,9 +15,11 @@ #include -#include "AFML/FocusManager.h" +#include #include +#include "AFML/FocusManager.h" + namespace alexaClientSDK { namespace afml { diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/AbstractAVSConnectionManager.h b/AVSCommon/AVS/include/AVSCommon/AVS/AbstractAVSConnectionManager.h index 9404795b19..22a2aad60e 100644 --- a/AVSCommon/AVS/include/AVSCommon/AVS/AbstractAVSConnectionManager.h +++ b/AVSCommon/AVS/include/AVSCommon/AVS/AbstractAVSConnectionManager.h @@ -58,16 +58,18 @@ class AbstractAVSConnectionManager : public sdkInterfaces::AVSConnectionManagerI * Utility function to update our local status variables. * * @param status The Connection Status. - * @param reason The reason the Connection Status changed. + * @param engineConnectionStatuses The detailed connection status for each engine. */ void updateConnectionStatus( ConnectionStatusObserverInterface::Status status, - ConnectionStatusObserverInterface::ChangedReason reason); + const std::vector& + engineConnectionStatuses); /** - * Utility function to notify all observers of the current Connection Status and Reason. + * Utility function to notify all observers of the current Connection Status of engines. + * @param avsConnectionStatusChanged value to indicate if default status changed or not */ - void notifyObservers(); + void notifyObservers(bool avsConnectionStatusChanged); /** * Removes all observers registered for Connection status notifications. @@ -80,8 +82,12 @@ class AbstractAVSConnectionManager : public sdkInterfaces::AVSConnectionManagerI /// The current connection status. @c m_mutex must be acquired before access. ConnectionStatusObserverInterface::Status m_connectionStatus; - /// The reason we changed to the current connection status. @c m_mutex must be acquired before access. - ConnectionStatusObserverInterface::ChangedReason m_connectionChangedReason; + /// The current Alexa Voice Services connection status. TODO STAR-562: once migrate to use new + /// onConnectionStatusChange API, this member variable should be removed. + ConnectionStatusObserverInterface::Status m_avsConnectionStatus; + + /// The detailed connection statuses for each engine + std::vector m_engineConnectionStatuses; /// Set of observers to notify when the connection status changes. @c m_mutex must be acquired before access. std::unordered_set> m_connectionStatusObservers; diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/Attachment/AttachmentManager.h b/AVSCommon/AVS/include/AVSCommon/AVS/Attachment/AttachmentManager.h index dc7080ab64..e3787ca215 100644 --- a/AVSCommon/AVS/include/AVSCommon/AVS/Attachment/AttachmentManager.h +++ b/AVSCommon/AVS/include/AVSCommon/AVS/Attachment/AttachmentManager.h @@ -71,9 +71,17 @@ class AttachmentManager : public AttachmentManagerInterface { IN_PROCESS }; + /** + * Factory for creating IN_PROCESS instances of AttachmentManagerInterface. + * + * @return An IN_PROCESS instances of AttachmentManagerInterface. + */ + static std::shared_ptr createInProcessAttachmentManagerInterface(); + /** * Constructor. * + * @deprecated * @param attachmentType The type of attachments which will be managed. */ AttachmentManager(AttachmentType attachmentType); diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/ActionsToDirectiveMapping.h b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/ActionsToDirectiveMapping.h new file mode 100644 index 0000000000..f4ef23da38 --- /dev/null +++ b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/ActionsToDirectiveMapping.h @@ -0,0 +1,96 @@ +/* + * 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_CAPABILITYSEMANTICS_ACTIONSTODIRECTIVEMAPPING_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYSEMANTICS_ACTIONSTODIRECTIVEMAPPING_H_ + +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { +namespace capabilitySemantics { + +/** + * This class represents an "ActionsToDirective" type "actionMapping" in a semantic annotation for a capability + * primitive. + * + * @sa https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/capability-primitives.html#semantic-annotation + */ +class ActionsToDirectiveMapping { +public: + /** + * The constructor. + */ + ActionsToDirectiveMapping(); + + /** + * Adds the specified action identifier to the "actions" array of this mapping object. The action identifier + * represents utterances that should trigger the directive specified in @c setDirective(). + * + * @note See the class-level link for supported action identifiers. + * + * @param action The identifier of the action to add to the "actions" array. + * @return @c true if the action was successfully added, else @c false. + */ + bool addAction(const std::string& actionId); + + /** + * Sets the "directive" field of this mapping object. The action IDs specified with calls to @c addAction() + * correspond to this directive name and payload. + * + * @param name The name of the directive mapped to the action(s). Must be a valid directive of the capability + * interface to which the semantics object belongs. + * @param payload The desired payload of the directive. + * @return @c true if the directive was successfully set, else @c false. + */ + bool setDirective(const std::string& name, const std::string& payload = "{}"); + + /** + * Checks whether this @c ActionsToDirectiveMapping is valid. + * + * @return @c true if valid, else @c false. + */ + bool isValid() const; + + /** + * Converts this @c ActionsToDirectiveMapping to a JSON string. + * + * @note This follows the AVS discovery message format. + * + * @return A JSON string of this @c ActionsToDirectiveMapping. + */ + std::string toJson() const; + +private: + /// Indicates an error in construction. + bool m_isValid; + + /// List of action IDs used in this mapping. + std::vector m_actions; + + /// The name of the directive used in this mapping. + std::string m_directiveName; + + /// The directive payload used in this mapping. + std::string m_directivePayload; +}; + +} // namespace capabilitySemantics +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYSEMANTICS_ACTIONSTODIRECTIVEMAPPING_H_ diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/CapabilitySemantics.h b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/CapabilitySemantics.h new file mode 100644 index 0000000000..cb91862df9 --- /dev/null +++ b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/CapabilitySemantics.h @@ -0,0 +1,106 @@ +/* + * 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_CAPABILITYSEMANTICS_CAPABILITYSEMANTICS_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYSEMANTICS_CAPABILITYSEMANTICS_H_ + +#include +#include + +#include "ActionsToDirectiveMapping.h" +#include "StatesToRangeMapping.h" +#include "StatesToValueMapping.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { +namespace capabilitySemantics { + +/** + * This class represents the 'semantics' object of a capability primitive definition. + * @see https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/capability-primitives.html#semantic-annotation + */ +class CapabilitySemantics { +public: + /** + * The default constructor. + */ + CapabilitySemantics() = default; + + /** + * Adds an @c ActionsToDirective type "actionMapping" to this semantics definition. + * + * @note A specific action ID may be used in only one 'actionMappings' object added with this method, but this + * method does not provide the validation. + * + * @param mapping The ActionsToDirective mapping represented as an @c ActionsToDirectiveMapping. + * @return @c true if adding the mapping was successful, else @c false. + */ + bool addActionsToDirectiveMapping(const ActionsToDirectiveMapping& mapping); + + /** + * Adds a @c StatesToValue type "stateMapping" to this semantics definition. + * + * @note A specific state ID may be used in only one 'stateMappings' object added with either this method or + * @c addStatesToRangeMapping(), but this method does not provide the validation. + * + * @param mapping The StatesToValue mapping represented as an @c StatesToValueMapping. + * @return @c true if adding the mapping was successful, else @c false. + */ + bool addStatesToValueMapping(const StatesToValueMapping& mapping); + + /** + * Adds a @c StatesToRange type "stateMapping" to this semantics definition. + * + * @note A specific state ID may be used in only one 'stateMappings' object added with either this method or + * @c addStatesToValueMapping(), but this method does not provide the validation. + * + * @param mapping The StatesToRange mapping represented as an @c StatesToRangeMapping. + * @return @c true if adding the mapping was successful, else @c false. + */ + bool addStatesToRangeMapping(const StatesToRangeMapping& mapping); + + /** + * Checks if the @c CapabilitySemantics is valid. + * + * @return @c true if valid, else @c false. + */ + bool isValid() const; + + /** + * Converts this semantics object to a JSON string. + * + * @note This follows the AVS discovery message format. + * + * @return A JSON string representation of this @c CapabilitySemantics. + */ + std::string toJson() const; + +private: + /// Vector holding the @c ActionsToDirectiveMapping action mappings. + std::vector m_actionsDirectiveMappings; + + /// Vector holding the @c StatesToValueMapping state mappings. + std::vector m_statesValueMappings; + + /// Vector holding the @c StatesToRangeMapping state mappings. + std::vector m_statesRangeMappings; +}; + +} // namespace capabilitySemantics +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYSEMANTICS_CAPABILITYSEMANTICS_H_ diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/StatesToRangeMapping.h b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/StatesToRangeMapping.h new file mode 100644 index 0000000000..205a45fd76 --- /dev/null +++ b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/StatesToRangeMapping.h @@ -0,0 +1,105 @@ +/* + * 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_CAPABILITYSEMANTICS_STATESTORANGEMAPPING_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYSEMANTICS_STATESTORANGEMAPPING_H_ + +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { +namespace capabilitySemantics { + +/** + * This class represents a "StatesToRange" type "stateMapping" in a semantic annotation for a capability primitive. + * @sa https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/capability-primitives.html#semantic-annotation + * + * @note This mapping may only be used if the corresponding @c CapabilitySemantics belongs to a @c RangeController + * capability instance. + */ +class StatesToRangeMapping { +public: + /** + * The constructor. + */ + StatesToRangeMapping(); + + /** + * Adds the specified state identifier to the "states" array of this mapping object. The state identifier + * represents utterances that correspond to the range specified in @c setRange(). + * + * @note See the class-level link for supported state identifiers. + * + * @param states The identifier of the state to add to the "states" array. + * @return @c true if the state was successfully added, else @c false. + */ + bool addState(const std::string& stateId); + + /** + * Sets the range used in this state mapping. + * + * @note The @a minValue and @a maxValue of the range must be within @c minimumValue + * and @c maximumValue configured in @c configuration.supportedRange for this @c RangeController instance. + * + * @param minValue The minimum value of the range mapped to the state(s). + * @param maxValue The minimum value of the range mapped to the state(s). + * @return @c true if the value was successfully set, else @c false. + */ + bool setRange(double minValue, double maxValue); + + /** + * Checks whether this @c StatesToRangeMapping is valid. + * + * @return @c true if valid, else @c false. + */ + bool isValid() const; + + /** + * Converts this @c StatesToRangeMapping to a JSON string. + * + * @note This follows the AVS discovery message format. + * + * @return A JSON string of this @c StatesToRangeMapping. + */ + std::string toJson() const; + +private: + /** + * Returns whether the range value for this mapping is initialized. + * + * @return @c true if @c setRange() was called with a valid value. + */ + bool isRangeSet() const; + + /// Indicates an error in construction. + bool m_isValid; + + /// List of state IDs used in this mapping. + std::vector m_states; + + /// The minimum range value used in this mapping. + double m_minValue; + + /// The maximum range value used in this mapping. + double m_maxValue; +}; + +} // namespace capabilitySemantics +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYSEMANTICS_STATESTORANGEMAPPING_H_ diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/StatesToValueMapping.h b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/StatesToValueMapping.h new file mode 100644 index 0000000000..890ecefd3e --- /dev/null +++ b/AVSCommon/AVS/include/AVSCommon/AVS/CapabilitySemantics/StatesToValueMapping.h @@ -0,0 +1,121 @@ +/* + * 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_CAPABILITYSEMANTICS_STATESTOVALUEMAPPING_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYSEMANTICS_STATESTOVALUEMAPPING_H_ + +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { +namespace capabilitySemantics { + +/** + * This class represents a "StatesToValue" type "stateMapping" in a semantic annotation for a capability primitive. + * + * @sa https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/capability-primitives.html#semantic-annotation + */ +class StatesToValueMapping { +public: + /** + * The constructor. + */ + StatesToValueMapping(); + + /** + * Adds the specified state identifier to the "states" array of this mapping object. The state identifier + * represents utterances that correspond to the value specified in @c setValue(). + * + * @note See the class-level link for supported state identifiers. + * + * @param states The identifier of the state to add to the "states" array. + * @return @c true if the state was successfully added, else @c false. + */ + bool addState(const std::string& stateId); + + /** + * Sets a string value used in this state mapping. + * + * @note The accepted value depends on the type of the capability interface to which this semantics object belongs. + * The @a value must match a configured value of the corresponding reportable state property for the capability. + * @sa https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/reportable-state-properties.html + * + * @param value The value mapped to the state(s). + * @return @c true if the value was successfully set, else @c false. + */ + bool setValue(const std::string& value); + + /** + * Sets a floating point value used in this state mapping. + * + * @note This @a value type is only supported for @c RangeController capability instances. + * The @a value must be between the @c minimumValue and @c maximumValue of @c configuration.supportedRange. + * @sa https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/alexa-rangecontroller.html + * + * @param value The value mapped to the state(s). + * @return @c true if the value was successfully set, else @c false. + */ + bool setValue(double value); + + /** + * Checks whether this @c StatesToValueMapping is valid. + * + * @return @c true if valid, else @c false. + */ + bool isValid() const; + + /** + * Converts this @c StatesToValueMapping to a JSON string. + * + * @note This follows the AVS discovery message format. + * + * @return A JSON string of this @c StatesToValueMapping. + */ + std::string toJson() const; + +private: + /** + * Returns whether the value for this mapping is initialized. + * + * @return @c true if one of the @c setValue() methods was called with a valid value. + */ + bool isValueSet() const; + + /// Indicates an error in construction. + bool m_isValid; + + /// List of state IDs used in this mapping. + std::vector m_states; + + /** + * The value used in this mapping. + * Only one of @c m_stringValue or @c m_doubleValue should be used per instance. + */ + std::string m_stringValue; + + /** + * The value used in this mapping. + * Only one of @c m_stringValue or @c m_doubleValue should be used per instance. + */ + double m_doubleValue; +}; + +} // namespace capabilitySemantics +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_CAPABILITYSEMANTICS_STATESTOVALUEMAPPING_H_ diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/DialogUXStateAggregator.h b/AVSCommon/AVS/include/AVSCommon/AVS/DialogUXStateAggregator.h index d27530893c..f35e2fb39f 100644 --- a/AVSCommon/AVS/include/AVSCommon/AVS/DialogUXStateAggregator.h +++ b/AVSCommon/AVS/include/AVSCommon/AVS/DialogUXStateAggregator.h @@ -46,6 +46,24 @@ class DialogUXStateAggregator , public sdkInterfaces::ConnectionStatusObserverInterface , public sdkInterfaces::InteractionModelRequestProcessingObserverInterface { public: + /** + * This timeout will be used to avoid going to the IDLE state immediately after receiving a message from AVS so + * that other UX states (such as speech starting) may be processed and propagated before going to IDLE. + */ + static constexpr std::chrono::milliseconds SHORT_TIMEOUT_FOR_THINKING_TO_IDLE = std::chrono::milliseconds{200}; + + /** + * This timeout will be used to time out from the THINKING state in case no messages arrive from AVS so that the + * client may move back to an IDLE state. + */ + static constexpr std::chrono::seconds LONG_TIMEOUT_FOR_THINKING_TO_IDLE = std::chrono::seconds{8}; + + /** + * This timeout will be used to time out from the LISTENING state in case the Request Processing Started (RPS) + * directive is not received from AVS so that the client may move back to an IDLE state. + */ + static constexpr std::chrono::seconds LONG_TIMEOUT_FOR_LISTENING_TO_IDLE = std::chrono::seconds{8}; + /** * Constructor. * @@ -56,11 +74,13 @@ class DialogUXStateAggregator * arrive from AVS. * @param timeoutForListeningToIdle This timeout will be used to time out from the LISTENING state in case the * Request Processing Started (RPS) directive is not received from AVS. + * @param shortTimeoutForThinkingToIdle This timeout will be used to avoid going to the IDLE state immediately */ DialogUXStateAggregator( std::shared_ptr metricRecorder = nullptr, - std::chrono::milliseconds timeoutForThinkingToIdle = std::chrono::seconds{8}, - std::chrono::milliseconds timeoutForListeningToIdle = std::chrono::seconds{8}); + std::chrono::milliseconds timeoutForThinkingToIdle = LONG_TIMEOUT_FOR_THINKING_TO_IDLE, + std::chrono::milliseconds timeoutForListeningToIdle = LONG_TIMEOUT_FOR_LISTENING_TO_IDLE, + std::chrono::milliseconds shortTimeoutForThinkingToIdle = SHORT_TIMEOUT_FOR_THINKING_TO_IDLE); /** * Adds an observer to be notified of UX state changes. @@ -111,14 +131,21 @@ class DialogUXStateAggregator * Sets the internal state to the new state. * * @param newState The new Alexa UX state. + * @return Whether the state transition was successful. */ - void setState(sdkInterfaces::DialogUXStateObserverInterface::DialogUXState newState); + bool executeSetState(sdkInterfaces::DialogUXStateObserverInterface::DialogUXState newState); /** * Sets the internal state to @c IDLE if both @c SpeechSynthesizer and @c AudioInputProcessor are in idle state. */ void tryEnterIdleState(); + /** + * 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(); + /** * Transitions the internal state from THINKING to IDLE. */ @@ -164,6 +191,12 @@ class DialogUXStateAggregator /// The timeout to be used for transitioning away from the THINKING state in case no messages are received. const std::chrono::milliseconds m_timeoutForThinkingToIdle; + /** + * This timeout will be used to avoid going to the IDLE state immediately after receiving a message from AVS so + * that other UX states (such as speech starting) may be processed and propagated. + */ + const std::chrono::milliseconds m_shortTimeoutForThinkingToIdle; + /// A timer to transition out of the THINKING state. avsCommon::utils::timing::Timer m_thinkingTimeoutTimer; @@ -172,7 +205,7 @@ class DialogUXStateAggregator /// The timeout to be used for transitioning away form the LISTENING state in case RPS (Request Processing Started) /// directive is not received. - const std::chrono::microseconds m_timeoutForListeningToIdle; + const std::chrono::milliseconds m_timeoutForListeningToIdle; /// A timer to transition out of the LISTENING state to IDLE state in case RPS (Request Processing Started) /// directive is not received. diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/ExceptionEncounteredSender.h b/AVSCommon/AVS/include/AVSCommon/AVS/ExceptionEncounteredSender.h index 9cc557797c..38f3f13059 100644 --- a/AVSCommon/AVS/include/AVSCommon/AVS/ExceptionEncounteredSender.h +++ b/AVSCommon/AVS/include/AVSCommon/AVS/ExceptionEncounteredSender.h @@ -32,9 +32,20 @@ namespace avs { */ class ExceptionEncounteredSender : public avsCommon::sdkInterfaces::ExceptionEncounteredSenderInterface { public: + /** + * Creates a new @c ExceptionEncounteredSenderInterface instance. + * + * @param messageSender The object to use for sending events. + * @return A @c std::shared_ptr to the new @c ExceptionEncounteredSenderInterface instance. + */ + static std::shared_ptr + createExceptionEncounteredSenderInterface( + const std::shared_ptr& messageSender); + /** * Creates a new @c ExceptionEncounteredSender instance. * + * @deprecated Use createExceptionEncounteredSenderInterface instead. * @param messageSender The object to use for sending events. * @return A @c std::unique_ptr to the new @c ExceptionEncounteredSender instance. */ diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/Initialization/AlexaClientSDKInit.h b/AVSCommon/AVS/include/AVSCommon/AVS/Initialization/AlexaClientSDKInit.h index a16da92ae2..6047280299 100644 --- a/AVSCommon/AVS/include/AVSCommon/AVS/Initialization/AlexaClientSDKInit.h +++ b/AVSCommon/AVS/include/AVSCommon/AVS/Initialization/AlexaClientSDKInit.h @@ -21,6 +21,8 @@ #include #include +#include +#include #include namespace alexaClientSDK { @@ -34,6 +36,7 @@ namespace initialization { class AlexaClientSDKInit { public: /** + * @deprecated v1.21.0. This method does not support some new features, such as low power mode. * Get a function to create an instance of AlexaClientSDKInit. * * @param jsonStreams Vector of @c istreams containing JSON documents from which @@ -48,6 +51,17 @@ class AlexaClientSDKInit { static std::function(std::shared_ptr)> getCreateAlexaClientSDKInit(const std::vector>& jsonStreams); + /** + * Get a function to create an instance of AlexaClientSDKInit. + * + * @note To enable low power mode, the PowerResourceManager must be added to the @c InitializationParameters. + * + * @param initParams The @c InitializationParameters. + * @return A function to create an instance of AlexaClientSDKInit. + */ + static std::function(std::shared_ptr)> + getCreateAlexaClientSDKInit(const std::shared_ptr& initParams); + /** * Destructor. */ @@ -61,6 +75,7 @@ class AlexaClientSDKInit { static bool isInitialized(); /** + * @deprecated v1.21.0 * Initialize the Alexa Client SDK. This must be called before any Alexa Client SDK modules are created. * * This function must be called before any threads in the process have been created by the @@ -80,6 +95,19 @@ class AlexaClientSDKInit { */ static bool initialize(const std::vector>& jsonStreams); + /** + * Initialize the Alexa Client SDK. This must be called before any Alexa Client SDK modules are created. + * + * This function must be called before any threads in the process have been created by the + * program; this function is not thread safe. This requirement is present because initialize() + * calls functions of other libraries that have the same requirements and thread safety. + * terminate() must be called for each initialize() called. + * + * @param initParams The @c InitializationParameters. + * @return Whether the initialization was successful. + */ + static bool initialize(const std::shared_ptr& initParams); + /** * Uninitialize the Alexa Client SDK. * diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/Initialization/InitializationParameters.h b/AVSCommon/AVS/include/AVSCommon/AVS/Initialization/InitializationParameters.h new file mode 100644 index 0000000000..51ffaeb78d --- /dev/null +++ b/AVSCommon/AVS/include/AVSCommon/AVS/Initialization/InitializationParameters.h @@ -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. + */ + +#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_INITIALIZATION_INITIALIZATIONPARAMETERS_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_INITIALIZATION_INITIALIZATIONPARAMETERS_H_ + +#include +#include +#include + +#include +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { +namespace initialization { + +/** + * A struct to contain the various parameters that are needed to initialize @c AlexaClientSDKInit. + */ +struct InitializationParameters { + /** + * Vector of @c istreams containing JSON documents from which + * to parse configuration parameters. Streams are processed in the order they appear in the vector. When a + * value appears in more than one JSON stream the last processed stream's value overwrites the previous value + * (and a debug log entry will be created). This allows for specifying default settings (by providing them + * first) and specifying the configuration from multiple sources (e.g. a separate stream for each component). + * Documentation of the JSON configuration format and methods to access the resulting global configuration + * can be found here: avsCommon::utils::configuration::ConfigurationNode. + */ + std::shared_ptr>> jsonStreams; + + /// The @c PowerResourceManagerInterface. This will be used for power management. + std::shared_ptr powerResourceManager; + + /// The @c TimerDelegateFactoryInterface. This will be used to inject custom implementations of @c + /// TimerDelegateInterface. + std::shared_ptr timerDelegateFactory; +}; + +} // namespace initialization +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_INITIALIZATION_INITIALIZATIONPARAMETERS_H_ diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/Initialization/InitializationParametersBuilder.h b/AVSCommon/AVS/include/AVSCommon/AVS/Initialization/InitializationParametersBuilder.h new file mode 100644 index 0000000000..70696e6de8 --- /dev/null +++ b/AVSCommon/AVS/include/AVSCommon/AVS/Initialization/InitializationParametersBuilder.h @@ -0,0 +1,84 @@ +/* + * 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_INITIALIZATION_INITIALIZATIONPARAMETERSBUILDER_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_INITIALIZATION_INITIALIZATIONPARAMETERSBUILDER_H_ + +#include "AVSCommon/AVS/Initialization/InitializationParameters.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { +namespace initialization { + +/** A class to contain the various parameters that are needed to initialize @c AlexaClientSDKInit. + */ +class InitializationParametersBuilder { +public: + /** + * Creates an instance of the @c InitializationParametersBuilder. + * + * @return A ptr to this builder. + */ + static std::unique_ptr create(); + + /** + * Add JSON streams. + * + * @param jsonStreams. + * @return This instance of the builder. + */ + InitializationParametersBuilder& withJsonStreams( + const std::shared_ptr>>& jsonStreams); + + /** + * Add @c PowerResourceManagerInterface. + * + * @param powerResourceManager The @c PowerResourceManagerInterface. + * @return This instance of the builder. + */ + InitializationParametersBuilder& withPowerResourceManager( + const std::shared_ptr& powerResourceManager); + + /** + * Add @c TimerDelegateFactoryInterface. + * + * @param timerDelegateFactory The @c TimerDelegateFactoryInterface. + * @return This instance of the builder. + */ + InitializationParametersBuilder& withTimerDelegateFactory( + const std::shared_ptr& timerDelegateFactory); + + /** + * Build the @c InitializationParameters object. + * + * @return If valid, an @c InitializationParameters object, otherwise nullptr. + */ + std::unique_ptr build(); + +private: + /// Constructor. + InitializationParametersBuilder(); + + /// The InitializationParameters. + InitializationParameters m_initParams; +}; + +} // namespace initialization +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_INITIALIZATION_INITIALIZATIONPARAMETERSBUILDER_H_ diff --git a/AVSCommon/AVS/include/AVSCommon/AVS/Initialization/SDKPrimitivesProvider.h b/AVSCommon/AVS/include/AVSCommon/AVS/Initialization/SDKPrimitivesProvider.h new file mode 100644 index 0000000000..5aa6816d25 --- /dev/null +++ b/AVSCommon/AVS/include/AVSCommon/AVS/Initialization/SDKPrimitivesProvider.h @@ -0,0 +1,103 @@ +/* + * 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_INITIALIZATION_SDKPRIMITIVESPROVIDER_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_INITIALIZATION_SDKPRIMITIVESPROVIDER_H_ + +#include +#include + +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { +namespace initialization { + +/** + * Provides primitives to components. + * This class should only be used for objects which are impractical to pass as an explicit dependency. + */ +class SDKPrimitivesProvider { +public: + /** + * Returns the Singleton instance of the SDKPrimitivesProvider. + * + * @return The SDKPrimitivesProvider instance. + */ + static std::shared_ptr getInstance(); + + /** + * Sets the @c TimerDelegateFactoryInterface. + * + * @return Whether this operation was successful. + */ + bool withTimerDelegateFactory( + std::shared_ptr timerDelegateFactory); + + /** + * Get the @c TimerDelegateFactoryInterface. + * + * @return The @c TimerDelegateFactoryInterface. + */ + std::shared_ptr getTimerDelegateFactory(); + + /** + * Initialize with the provided members. + * + * @return Whether this operation was successful. + */ + bool initialize(); + + /** + * Initialize. + * + * @return Whether this is currently initialized. + */ + bool isInitialized() const; + + /** + * Reset all configured properties. + */ + void reset(); + + /** + * Resets all configured properties. Resets the Singleton instance. + */ + void terminate(); + +private: + /// Constructor. + SDKPrimitivesProvider(); + + /// The Singleton instance. + static std::shared_ptr m_provider; + + /// The mutex. + static std::mutex m_mutex; + + /// Whether or not it is initialized. + bool m_initialized; + + /// The @c TimerDelegateFactoryInterface. + std::shared_ptr m_timerDelegateFactory; +}; + +} // namespace initialization +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_AVS_INCLUDE_AVSCOMMON_AVS_INITIALIZATION_SDKPRIMITIVESPROVIDER_H_ diff --git a/AVSCommon/AVS/src/AbstractAVSConnectionManager.cpp b/AVSCommon/AVS/src/AbstractAVSConnectionManager.cpp index d3dd456bc2..280a7391c5 100644 --- a/AVSCommon/AVS/src/AbstractAVSConnectionManager.cpp +++ b/AVSCommon/AVS/src/AbstractAVSConnectionManager.cpp @@ -35,8 +35,12 @@ static const std::string TAG("AbstractAVSConnectionManager"); AbstractAVSConnectionManager::AbstractAVSConnectionManager( std::unordered_set> observers) : m_connectionStatus{ConnectionStatusObserverInterface::Status::DISCONNECTED}, - m_connectionChangedReason{ConnectionStatusObserverInterface::ChangedReason::ACL_CLIENT_REQUEST}, + m_avsConnectionStatus{ConnectionStatusObserverInterface::Status::DISCONNECTED}, m_connectionStatusObservers{observers} { + m_engineConnectionStatuses.emplace_back( + avsCommon::sdkInterfaces::ENGINE_TYPE_ALEXA_VOICE_SERVICES, + ConnectionStatusObserverInterface::ChangedReason::ACL_CLIENT_REQUEST, + ConnectionStatusObserverInterface::Status::DISCONNECTED); } void AbstractAVSConnectionManager::addConnectionStatusObserver( @@ -48,12 +52,28 @@ void AbstractAVSConnectionManager::addConnectionStatusObserver( std::unique_lock lock{m_mutex}; auto localStatus = m_connectionStatus; - auto localReason = m_connectionChangedReason; + auto localEngineConnectionStatuses = m_engineConnectionStatuses; bool addedOk = m_connectionStatusObservers.insert(observer).second; lock.unlock(); if (addedOk) { - observer->onConnectionStatusChanged(localStatus, localReason); + // call new onConnectionStatusChanged API + for (auto engineStatus : localEngineConnectionStatuses) { + (void)engineStatus; + ACSDK_DEBUG9(LX(__func__) + .d("engineType", engineStatus.engineType) + .d("status", engineStatus.status) + .d("reason", engineStatus.reason)); + } + observer->onConnectionStatusChanged(localStatus, localEngineConnectionStatuses); + // call old onConnectionStatusChanged API on AVS connection + for (const auto& status : localEngineConnectionStatuses) { + if (status.engineType == avsCommon::sdkInterfaces::ENGINE_TYPE_ALEXA_VOICE_SERVICES) { + ACSDK_DEBUG9(LX(__func__).d("status", status.status).d("reason", status.reason)); + observer->onConnectionStatusChanged(status.status, status.reason); + break; + } + } } } @@ -69,25 +89,53 @@ void AbstractAVSConnectionManager::removeConnectionStatusObserver( } void AbstractAVSConnectionManager::updateConnectionStatus( - ConnectionStatusObserverInterface::Status status, - ConnectionStatusObserverInterface::ChangedReason reason) { + alexaClientSDK::avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status status, + const std::vector& + engineConnectionStatuses) { std::unique_lock lock{m_mutex}; m_connectionStatus = status; - m_connectionChangedReason = reason; + bool avsConnectionStatusChanged = false; + for (const auto& engineStatus : engineConnectionStatuses) { + if (engineStatus.engineType == avsCommon::sdkInterfaces::ENGINE_TYPE_ALEXA_VOICE_SERVICES) { + avsConnectionStatusChanged = m_avsConnectionStatus != engineStatus.status; + m_avsConnectionStatus = engineStatus.status; + break; + } + } + m_engineConnectionStatuses = engineConnectionStatuses; lock.unlock(); - notifyObservers(); + notifyObservers(avsConnectionStatusChanged); } -void AbstractAVSConnectionManager::notifyObservers() { +void AbstractAVSConnectionManager::notifyObservers(bool avsConnectionStatusChanged) { std::unique_lock lock{m_mutex}; auto observers = m_connectionStatusObservers; auto localStatus = m_connectionStatus; - auto localReason = m_connectionChangedReason; + auto localEngineConnectionStatuses = m_engineConnectionStatuses; lock.unlock(); - for (auto observer : observers) { - observer->onConnectionStatusChanged(localStatus, localReason); + for (auto engineStatus : localEngineConnectionStatuses) { + (void)engineStatus; + ACSDK_DEBUG5(LX(__func__) + .m("EngineConnectionStatusDetail") + .d("engineType", engineStatus.engineType) + .d("status", engineStatus.status) + .d("reason", engineStatus.reason)); + } + for (const auto& observer : observers) { + // call new onConnectionStatusChanged API + observer->onConnectionStatusChanged(localStatus, localEngineConnectionStatuses); + // call old onConnectionStatusChanged API on AVS connection + if (avsConnectionStatusChanged) { + for (const auto& status : localEngineConnectionStatuses) { + if (status.engineType == avsCommon::sdkInterfaces::ENGINE_TYPE_ALEXA_VOICE_SERVICES) { + ACSDK_DEBUG9(LX(__func__).d("status", status.status).d("reason", status.reason)); + observer->onConnectionStatusChanged(status.status, status.reason); + break; + } + } + } } } diff --git a/AVSCommon/AVS/src/AlexaClientSDKInit.cpp b/AVSCommon/AVS/src/AlexaClientSDKInit.cpp index d958631a20..3dd4572e2d 100644 --- a/AVSCommon/AVS/src/AlexaClientSDKInit.cpp +++ b/AVSCommon/AVS/src/AlexaClientSDKInit.cpp @@ -16,8 +16,12 @@ #include #include "AVSCommon/AVS/Initialization/AlexaClientSDKInit.h" +#include "AVSCommon/AVS/Initialization/InitializationParametersBuilder.h" +#include "AVSCommon/AVS/Initialization/SDKPrimitivesProvider.h" #include "AVSCommon/Utils/Configuration/ConfigurationNode.h" #include "AVSCommon/Utils/Logger/Logger.h" +#include "AVSCommon/Utils/Power/NoOpPowerResourceManager.h" +#include "AVSCommon/Utils/Power/PowerMonitor.h" #include "AVSCommon/Utils/SDKVersion.h" namespace alexaClientSDK { @@ -53,6 +57,22 @@ std::function(std::shared_ptr(std::shared_ptr)> AlexaClientSDKInit:: + getCreateAlexaClientSDKInit(const std::shared_ptr& initParams) { + return [initParams](std::shared_ptr logger) -> std::shared_ptr { + if (!logger) { + ACSDK_ERROR(LX("getCreateAlexaClientSDKInitFailed").d("reason", "nullLogger")); + return nullptr; + } + + if (!initialize(initParams)) { + ACSDK_ERROR(LX("getCreateAlexaClientSDKInitFailed").d("reason", "initializeFailed")); + return nullptr; + } + return std::shared_ptr(new AlexaClientSDKInit); + }; +} + /** * Destructor. */ @@ -65,14 +85,33 @@ bool AlexaClientSDKInit::isInitialized() { } bool AlexaClientSDKInit::initialize(const std::vector>& jsonStreams) { + auto builder = InitializationParametersBuilder::create(); + if (!builder) { + ACSDK_ERROR(LX("initializeFailed").d("reason", "nullBuilder")); + return false; + } + + // Copy to preserve backwards compatibility. + auto jsonStreamsPtr = std::make_shared>>(jsonStreams); + builder->withJsonStreams(jsonStreamsPtr); + auto initParams = builder->build(); + return initialize(std::move(initParams)); +} + +bool AlexaClientSDKInit::initialize(const std::shared_ptr& initParams) { ACSDK_INFO(LX(__func__).d("sdkversion", avsCommon::utils::sdkVersion::getCurrentVersion())); + if (!initParams) { + ACSDK_ERROR(LX("initializeFailed").d("reason", "nullInitParams")); + return false; + } + if (!(curl_version_info(CURLVERSION_NOW)->features & CURL_VERSION_HTTP2)) { ACSDK_ERROR(LX("initializeFailed").d("reason", "curlDoesNotSupportHTTP2")); return false; } - if (!utils::configuration::ConfigurationNode::initialize(jsonStreams)) { + if (!utils::configuration::ConfigurationNode::initialize(*initParams->jsonStreams)) { ACSDK_ERROR(LX("initializeFailed").d("reason", "ConfigurationNode::initializeFailed")); return false; } @@ -83,6 +122,41 @@ bool AlexaClientSDKInit::initialize(const std::vectortimerDelegateFactory; + if (!timerDelegateFactory) { + ACSDK_ERROR(LX("initializeFailed").d("reason", "nullTimerDelegateFactory")); + return false; + } + +#ifdef ENABLE_LPM + auto powerResourceManager = initParams->powerResourceManager; + if (powerResourceManager) { + utils::power::PowerMonitor::getInstance()->activate(powerResourceManager); + + if (!timerDelegateFactory->supportsLowPowerMode()) { + ACSDK_ERROR(LX("initializeFailed") + .d("reason", "unsupportedTimerDelegateFactory") + .d("missing", "lowPowerModeSupport")); + return false; + } + } else { + // Logic in PowerMonitor has fallback for the non active case. + ACSDK_ERROR( + LX(__func__).d("reason", "nullPowerResourceManager").m("Falling back to non-activated PowerMonitor")); + } +#endif + + auto primitivesProvider = SDKPrimitivesProvider::getInstance(); + if (!primitivesProvider) { + ACSDK_ERROR(LX("initializeFailed").d("reason", "nullSDKPrimitivesProvider")); + return false; + } + + primitivesProvider->withTimerDelegateFactory(timerDelegateFactory); + if (!primitivesProvider->initialize()) { + return false; + } + g_isInitialized++; return true; } @@ -95,6 +169,11 @@ void AlexaClientSDKInit::uninitialize() { g_isInitialized--; curl_global_cleanup(); utils::configuration::ConfigurationNode::uninitialize(); + utils::power::PowerMonitor::getInstance()->deactivate(); + auto primitivesProvider = SDKPrimitivesProvider::getInstance(); + if (primitivesProvider) { + primitivesProvider->terminate(); + } } } // namespace initialization diff --git a/AVSCommon/AVS/src/Attachment/AttachmentManager.cpp b/AVSCommon/AVS/src/Attachment/AttachmentManager.cpp index 190c8ce849..8d4e23e9ed 100644 --- a/AVSCommon/AVS/src/Attachment/AttachmentManager.cpp +++ b/AVSCommon/AVS/src/Attachment/AttachmentManager.cpp @@ -50,6 +50,10 @@ AttachmentManager::AttachmentManagementDetails::AttachmentManagementDetails() : creationTime{std::chrono::steady_clock::now()} { } +std::shared_ptr AttachmentManager::createInProcessAttachmentManagerInterface() { + return std::make_shared(AttachmentType::IN_PROCESS); +} + AttachmentManager::AttachmentManager(AttachmentType attachmentType) : m_attachmentType{attachmentType}, m_attachmentExpirationMinutes{ATTACHMENT_MANAGER_TIMOUT_MINUTES_DEFAULT} { diff --git a/AVSCommon/AVS/src/CapabilitySemantics/ActionsToDirectiveMapping.cpp b/AVSCommon/AVS/src/CapabilitySemantics/ActionsToDirectiveMapping.cpp new file mode 100644 index 0000000000..ba9efafac4 --- /dev/null +++ b/AVSCommon/AVS/src/CapabilitySemantics/ActionsToDirectiveMapping.cpp @@ -0,0 +1,124 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { +namespace capabilitySemantics { + +/// The "@type" key of mapping object JSON. +static const std::string TYPE_KEY("@type"); + +/// The "ActionsToDirective" value of mapping object JSON. +static const std::string MAPPING_TYPE_VALUE("ActionsToDirective"); + +/// The "actions" key of mapping object JSON. +static const std::string ACTIONS_KEY("actions"); + +/// The "directive" key of mapping object JSON. +static const std::string DIRECTIVE_KEY("directive"); + +/// The "name" key of mapping object JSON. +static const std::string NAME_KEY("name"); + +/// The "payload" key of mapping object JSON. +static const std::string PAYLOAD_KEY("payload"); + +/// Empty JSON object. +static const std::string EMPTY_JSON("{}"); + +/// String to identify log entries originating from this file. +static const std::string TAG("ActionsToDirectiveMapping"); + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param The event string for this @c LogEntry. + */ +#define LX(event) utils::logger::LogEntry(TAG, event) + +ActionsToDirectiveMapping::ActionsToDirectiveMapping() : m_isValid{true} { +} + +bool ActionsToDirectiveMapping::addAction(const std::string& actionId) { + if (actionId.empty()) { + ACSDK_ERROR(LX("addActionFailed").d("reason", "emptyActionId")); + m_isValid = false; + return false; + } + if (std::find(m_actions.begin(), m_actions.end(), actionId) != m_actions.end()) { + ACSDK_ERROR(LX("addActionFailed").d("reason", "duplicateActionId").d("actionId", actionId)); + m_isValid = false; + return false; + } + m_actions.push_back(actionId); + return true; +} + +bool ActionsToDirectiveMapping::setDirective(const std::string& directive, const std::string& payload) { + if (directive.empty()) { + ACSDK_ERROR(LX("setDirectiveFailed").d("reason", "emptyDirectiveName")); + m_isValid = false; + return false; + } + if (payload.empty()) { + ACSDK_ERROR(LX("setDirectiveFailed").d("reason", "emptyPayload")); + m_isValid = false; + return false; + } + if (!m_directiveName.empty()) { + ACSDK_ERROR(LX("setDirectiveFailed").d("directive", directive).d("reason", "directiveAlreadySet")); + m_isValid = false; + return false; + } + m_directiveName = directive; + m_directivePayload = payload; + return true; +} + +bool ActionsToDirectiveMapping::isValid() const { + return m_isValid && !m_actions.empty() && !m_directiveName.empty(); +} + +std::string ActionsToDirectiveMapping::toJson() const { + if (!isValid()) { + ACSDK_ERROR(LX("toJsonFailed").d("reason", "invalidActionsToDirectiveMapping")); + return EMPTY_JSON; + } + + utils::json::JsonGenerator jsonGenerator; + jsonGenerator.addMember(TYPE_KEY, MAPPING_TYPE_VALUE); + jsonGenerator.addStringArray(ACTIONS_KEY, m_actions); + jsonGenerator.startObject(DIRECTIVE_KEY); + jsonGenerator.addMember(NAME_KEY, m_directiveName); + bool parseSuccess = jsonGenerator.addRawJsonMember(PAYLOAD_KEY, m_directivePayload, true); + if (!parseSuccess) { + ACSDK_ERROR(LX("toJsonFailed").d("reason", "payloadNotValidJson")); + return EMPTY_JSON; + } + jsonGenerator.finishObject(); // directive + return jsonGenerator.toString(); +} + +} // namespace capabilitySemantics +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/AVS/src/CapabilitySemantics/CapabilitySemantics.cpp b/AVSCommon/AVS/src/CapabilitySemantics/CapabilitySemantics.cpp new file mode 100644 index 0000000000..58d4fa356b --- /dev/null +++ b/AVSCommon/AVS/src/CapabilitySemantics/CapabilitySemantics.cpp @@ -0,0 +1,111 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { +namespace capabilitySemantics { + +/// The "actionMappings" key. +static const std::string ACTION_MAPPINGS_KEY("actionMappings"); + +/// The "stateMappings" key. +static const std::string STATE_MAPPINGS_KEY("stateMappings"); + +/// Empty JSON object. +static const std::string EMPTY_JSON("{}"); + +/// String to identify log entries originating from this file. +static const std::string TAG("CapabilitySemantics"); + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param The event string for this @c LogEntry. + */ +#define LX(event) utils::logger::LogEntry(TAG, event) + +bool CapabilitySemantics::addActionsToDirectiveMapping(const ActionsToDirectiveMapping& mapping) { + if (mapping.isValid()) { + m_actionsDirectiveMappings.push_back(mapping); + return true; + } else { + ACSDK_ERROR(LX("addActionsToDirectiveMappingFailed").d("reason", "invalidMapping")); + return false; + } +} + +bool CapabilitySemantics::addStatesToValueMapping(const StatesToValueMapping& mapping) { + if (mapping.isValid()) { + m_statesValueMappings.push_back(mapping); + return true; + } else { + ACSDK_ERROR(LX("addStatesToValueMappingFailed").d("reason", "invalidMapping")); + return false; + } +} + +bool CapabilitySemantics::addStatesToRangeMapping(const StatesToRangeMapping& mapping) { + if (mapping.isValid()) { + m_statesRangeMappings.push_back(mapping); + return true; + } else { + ACSDK_ERROR(LX("addStatesToRangeMappingFailed").d("reason", "invalidMapping")); + return false; + } +} + +bool CapabilitySemantics::isValid() const { + // Semantics requires at least one of 'actionMappings' or 'stateMappings' + return !m_actionsDirectiveMappings.empty() || !m_statesValueMappings.empty() || !m_statesRangeMappings.empty(); +} + +std::string CapabilitySemantics::toJson() const { + if (!isValid()) { + ACSDK_ERROR(LX("toJsonFailed").d("reason", "invalidCapabilitySemantics")); + return EMPTY_JSON; + } + + utils::json::JsonGenerator jsonGenerator; + if (!m_actionsDirectiveMappings.empty()) { + std::vector actionMappingJsonList; + for (const auto& mapping : m_actionsDirectiveMappings) { + actionMappingJsonList.push_back(mapping.toJson()); + } + jsonGenerator.addMembersArray(ACTION_MAPPINGS_KEY, actionMappingJsonList); + } + if (!m_statesValueMappings.empty() || !m_statesRangeMappings.empty()) { + std::vector stateMappingJsonList; + for (const auto& mapping : m_statesValueMappings) { + stateMappingJsonList.push_back(mapping.toJson()); + } + for (const auto& mapping : m_statesRangeMappings) { + stateMappingJsonList.push_back(mapping.toJson()); + } + jsonGenerator.addMembersArray(STATE_MAPPINGS_KEY, stateMappingJsonList); + } + return jsonGenerator.toString(); +} + +} // namespace capabilitySemantics +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/AVS/src/CapabilitySemantics/StatesToRangeMapping.cpp b/AVSCommon/AVS/src/CapabilitySemantics/StatesToRangeMapping.cpp new file mode 100644 index 0000000000..908c7732e2 --- /dev/null +++ b/AVSCommon/AVS/src/CapabilitySemantics/StatesToRangeMapping.cpp @@ -0,0 +1,126 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { +namespace capabilitySemantics { + +/// The "@type" key of mapping object JSON. +static const std::string TYPE_KEY("@type"); + +/// The "StatesToRange" mapping type. +static const std::string MAPPING_TYPE_VALUE("StatesToRange"); + +/// The "states" key of mapping object JSON. +static const std::string STATES_KEY("states"); + +/// The "range" key of mapping object JSON. +static const std::string RANGE_KEY("range"); + +/// The "minimumValue" key of mapping object JSON. +static const std::string MIN_VALUE_KEY("minimumValue"); + +/// The "maximumValue" key of mapping object JSON. +static const std::string MAX_VALUE_KEY("maximumValue"); + +/// Indicates @c m_minValue or @c m_maxValue is not initialized. +static constexpr double UNINITIALIZED_DOUBLE = std::numeric_limits::min(); + +/// Empty JSON object. +static const std::string EMPTY_JSON("{}"); + +/// String to identify log entries originating from this file. +static const std::string TAG("StatesToRangeMapping"); + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param The event string for this @c LogEntry. + */ +#define LX(event) utils::logger::LogEntry(TAG, event) + +StatesToRangeMapping::StatesToRangeMapping() : + m_isValid{true}, + m_minValue{UNINITIALIZED_DOUBLE}, + m_maxValue{UNINITIALIZED_DOUBLE} { +} + +bool StatesToRangeMapping::addState(const std::string& stateId) { + if (stateId.empty()) { + ACSDK_ERROR(LX("addStateFailed").d("reason", "emptyStateId")); + m_isValid = false; + return false; + } + if (std::find(m_states.begin(), m_states.end(), stateId) != m_states.end()) { + ACSDK_ERROR(LX("addStateFailed").d("reason", "duplicateStateId").d("stateId", stateId)); + m_isValid = false; + return false; + } + m_states.push_back(stateId); + return true; +} + +bool StatesToRangeMapping::setRange(double minValue, double maxValue) { + if (isRangeSet()) { + ACSDK_ERROR(LX("setRangeFailed").d("reason", "rangeAlreadySet")); + m_isValid = false; + return false; + } + if (minValue > maxValue) { + ACSDK_ERROR(LX("setRangeFailed").d("reason", "minGreaterThanMax")); + m_isValid = false; + return false; + } + m_minValue = minValue; + m_maxValue = maxValue; + return true; +} + +bool StatesToRangeMapping::isValid() const { + return m_isValid && isRangeSet() && !m_states.empty(); +} + +std::string StatesToRangeMapping::toJson() const { + if (!isValid()) { + ACSDK_ERROR(LX("toJsonFailed").d("reason", "invalidStatesToRangeMapping")); + return EMPTY_JSON; + } + + utils::json::JsonGenerator jsonGenerator; + jsonGenerator.addMember(TYPE_KEY, MAPPING_TYPE_VALUE); + jsonGenerator.addStringArray(STATES_KEY, m_states); + jsonGenerator.startObject(RANGE_KEY); + jsonGenerator.addMember(MIN_VALUE_KEY, m_minValue); + jsonGenerator.addMember(MAX_VALUE_KEY, m_maxValue); + jsonGenerator.finishObject(); + return jsonGenerator.toString(); +} + +bool StatesToRangeMapping::isRangeSet() const { + return m_minValue != UNINITIALIZED_DOUBLE && m_maxValue != UNINITIALIZED_DOUBLE; +} + +} // namespace capabilitySemantics +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/AVS/src/CapabilitySemantics/StatesToValueMapping.cpp b/AVSCommon/AVS/src/CapabilitySemantics/StatesToValueMapping.cpp new file mode 100644 index 0000000000..05d1ca6b12 --- /dev/null +++ b/AVSCommon/AVS/src/CapabilitySemantics/StatesToValueMapping.cpp @@ -0,0 +1,133 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { +namespace capabilitySemantics { + +/// The "@type" key of mapping object JSON. +static const std::string TYPE_KEY("@type"); + +/// The "StatesToValue" mapping type. +static const std::string MAPPING_TYPE_VALUE("StatesToValue"); + +/// The "states" key of mapping object JSON. +static const std::string STATES_KEY("states"); + +/// The "value" key of mapping object JSON. +static const std::string VALUE_KEY("value"); + +/// Indicates @c m_doubleValue is not initialized. +static constexpr double UNINITIALIZED_DOUBLE = std::numeric_limits::min(); + +/// Indicates @c m_stringValue is not initialized. +static std::string UNINITIALIZED_STRING = ""; + +/// Empty JSON object. +static const std::string EMPTY_JSON("{}"); + +/// String to identify log entries originating from this file. +static const std::string TAG("StatesToValueMapping"); + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param The event string for this @c LogEntry. + */ +#define LX(event) utils::logger::LogEntry(TAG, event) + +StatesToValueMapping::StatesToValueMapping() : + m_isValid{true}, + m_stringValue{UNINITIALIZED_STRING}, + m_doubleValue{UNINITIALIZED_DOUBLE} { +} + +bool StatesToValueMapping::addState(const std::string& stateId) { + if (stateId.empty()) { + ACSDK_ERROR(LX("addStateFailed").d("reason", "emptyStateId")); + m_isValid = false; + return false; + } + if (std::find(m_states.begin(), m_states.end(), stateId) != m_states.end()) { + ACSDK_ERROR(LX("addStateFailed").d("reason", "duplicateStateId").d("stateId", stateId)); + m_isValid = false; + return false; + } + m_states.push_back(stateId); + return true; +} + +bool StatesToValueMapping::setValue(const std::string& value) { + if (isValueSet()) { + ACSDK_ERROR(LX("setValueFailed").d("reason", "valueAlreadySet")); + m_isValid = false; + return false; + } + if (value.empty()) { + ACSDK_ERROR(LX("setValueFailed").d("reason", "emptyValue")); + m_isValid = false; + return false; + } + m_stringValue = value; + return true; +} + +bool StatesToValueMapping::setValue(double value) { + if (isValueSet()) { + ACSDK_ERROR(LX("setValueFailed").d("reason", "valueAlreadySet")); + m_isValid = false; + return false; + } + m_doubleValue = value; + return true; +} + +bool StatesToValueMapping::isValid() const { + return m_isValid && isValueSet() && !m_states.empty(); +} + +std::string StatesToValueMapping::toJson() const { + if (!isValid()) { + ACSDK_ERROR(LX("toJsonFailed").d("reason", "invalidStatesToValueMapping")); + return EMPTY_JSON; + } + + utils::json::JsonGenerator jsonGenerator; + jsonGenerator.addMember(TYPE_KEY, MAPPING_TYPE_VALUE); + jsonGenerator.addStringArray(STATES_KEY, m_states); + if (m_stringValue != UNINITIALIZED_STRING) { + jsonGenerator.addMember(VALUE_KEY, m_stringValue); + } else { + jsonGenerator.addMember(VALUE_KEY, m_doubleValue); + } + return jsonGenerator.toString(); +} + +bool StatesToValueMapping::isValueSet() const { + return m_stringValue != UNINITIALIZED_STRING || m_doubleValue != UNINITIALIZED_DOUBLE; +} + +} // namespace capabilitySemantics +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/AVS/src/DialogUXStateAggregator.cpp b/AVSCommon/AVS/src/DialogUXStateAggregator.cpp index babb3ed43a..57c2e89ba5 100644 --- a/AVSCommon/AVS/src/DialogUXStateAggregator.cpp +++ b/AVSCommon/AVS/src/DialogUXStateAggregator.cpp @@ -34,11 +34,6 @@ static const std::string TAG("DialogUXStateAggregator"); */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) -/** - * A short timeout that is used to avoid going to the IDLE state immediately while waiting for other state changes. - */ -static const std::chrono::milliseconds SHORT_TIMEOUT{200}; - /// Custom Metrics prefix used by DialogUXStateAggregator. static const std::string CUSTOM_METRIC_PREFIX = "CUSTOM-"; @@ -48,6 +43,10 @@ static const std::string LISTENING_TIMEOUT_EXPIRES = "LISTENING_TIMEOUT_EXPIRES" /// error metric for Thinking timeout expires static const std::string THINKING_TIMEOUT_EXPIRES = "THINKING_TIMEOUT_EXPIRES"; +constexpr std::chrono::milliseconds DialogUXStateAggregator::SHORT_TIMEOUT_FOR_THINKING_TO_IDLE; +constexpr std::chrono::seconds DialogUXStateAggregator::LONG_TIMEOUT_FOR_THINKING_TO_IDLE; +constexpr std::chrono::seconds DialogUXStateAggregator::LONG_TIMEOUT_FOR_LISTENING_TO_IDLE; + /** * Submits a metric of given event name * @param metricRecorder The @c MetricRecorderInterface which records Metric events @@ -73,13 +72,19 @@ static void submitMetric(const std::shared_ptr& metricR DialogUXStateAggregator::DialogUXStateAggregator( std::shared_ptr metricRecorder, std::chrono::milliseconds timeoutForThinkingToIdle, - std::chrono::milliseconds timeoutForListeningToIdle) : + std::chrono::milliseconds timeoutForListeningToIdle, + std::chrono::milliseconds shortTimeoutForThinkingToIdle) : m_metricRecorder{metricRecorder}, m_currentState{DialogUXStateObserverInterface::DialogUXState::IDLE}, m_timeoutForThinkingToIdle{timeoutForThinkingToIdle}, + m_shortTimeoutForThinkingToIdle{shortTimeoutForThinkingToIdle}, m_timeoutForListeningToIdle{timeoutForListeningToIdle}, m_speechSynthesizerState{SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED}, m_audioInputProcessorState{AudioInputProcessorObserverInterface::State::IDLE} { + ACSDK_DEBUG8(LX("timeout values in milliseconds") + .d("m_timeoutForThinkingToIdle", m_timeoutForThinkingToIdle.count()) + .d("m_shortTimeoutForThinkingToIdle", m_shortTimeoutForThinkingToIdle.count()) + .d("m_timeoutForListeningToIdle", m_timeoutForListeningToIdle.count())); } void DialogUXStateAggregator::addObserver(std::shared_ptr observer) { @@ -111,20 +116,22 @@ void DialogUXStateAggregator::onStateChanged(AudioInputProcessorObserverInterfac return; case AudioInputProcessorObserverInterface::State::RECOGNIZING: onActivityStarted(); - setState(DialogUXStateObserverInterface::DialogUXState::LISTENING); + executeSetState(DialogUXStateObserverInterface::DialogUXState::LISTENING); return; case AudioInputProcessorObserverInterface::State::EXPECTING_SPEECH: onActivityStarted(); - setState(DialogUXStateObserverInterface::DialogUXState::EXPECTING); + executeSetState(DialogUXStateObserverInterface::DialogUXState::EXPECTING); return; case AudioInputProcessorObserverInterface::State::BUSY: - setState(DialogUXStateObserverInterface::DialogUXState::LISTENING); - if (!m_listeningTimeoutTimer - .start( - m_timeoutForListeningToIdle, - std::bind(&DialogUXStateAggregator::transitionFromListeningTimedOut, this)) - .valid()) { - ACSDK_ERROR(LX("failedToStartTimerFromListeningToIdle")); + if (executeSetState(DialogUXStateObserverInterface::DialogUXState::LISTENING) || + DialogUXStateObserverInterface::DialogUXState::LISTENING == m_currentState) { + if (!m_listeningTimeoutTimer + .start( + m_timeoutForListeningToIdle, + std::bind(&DialogUXStateAggregator::transitionFromListeningTimedOut, this)) + .valid()) { + ACSDK_ERROR(LX("failedToStartTimerFromListeningToIdle")); + } } return; } @@ -143,7 +150,7 @@ void DialogUXStateAggregator::onStateChanged( switch (state) { case SpeechSynthesizerObserverInterface::SpeechSynthesizerState::PLAYING: onActivityStarted(); - setState(DialogUXStateObserverInterface::DialogUXState::SPEAKING); + executeSetState(DialogUXStateObserverInterface::DialogUXState::SPEAKING); return; case SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED: case SpeechSynthesizerObserverInterface::SpeechSynthesizerState::INTERRUPTED: @@ -160,9 +167,15 @@ 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 @@ -171,7 +184,8 @@ void DialogUXStateAggregator::receive(const std::string& contextId, const std::s */ m_thinkingTimeoutTimer.stop(); m_thinkingTimeoutTimer.start( - SHORT_TIMEOUT, std::bind(&DialogUXStateAggregator::transitionFromThinkingTimedOut, this)); + m_shortTimeoutForThinkingToIdle, + std::bind(&DialogUXStateAggregator::transitionFromThinkingTimedOut, this)); } }); } @@ -181,7 +195,7 @@ void DialogUXStateAggregator::onConnectionStatusChanged( const ConnectionStatusObserverInterface::ChangedReason reason) { m_executor.submit([this, status]() { if (status != avsCommon::sdkInterfaces::ConnectionStatusObserverInterface::Status::CONNECTED) { - setState(DialogUXStateObserverInterface::DialogUXState::IDLE); + executeSetState(DialogUXStateObserverInterface::DialogUXState::IDLE); } }); } @@ -200,7 +214,7 @@ void DialogUXStateAggregator::onRequestProcessingStarted() { ACSDK_WARN(LX("onRequestProcessingStartedLambda").d("reason", "transitioningFromIdle")); /* FALL-THROUGH */ case DialogUXStateObserverInterface::DialogUXState::LISTENING: - setState(DialogUXStateObserverInterface::DialogUXState::THINKING); + executeSetState(DialogUXStateObserverInterface::DialogUXState::THINKING); if (!m_thinkingTimeoutTimer .start( @@ -219,13 +233,9 @@ void DialogUXStateAggregator::onRequestProcessingStarted() { } void DialogUXStateAggregator::onRequestProcessingCompleted() { - // No-op - /* - * No particular processing is needed for this directive. The RequestProcessCompleted directive exists in the - * Interaction Model 1.1 to let AVS activate a logic that stops the thinking mode without any other semantic. But - * the specification is such that any directive will interrupt the thinking mode. So here we are simply confirming - * that this directive is supported. - */ + // If receive() calls occur before onRequestProcessStarted() happens, we need to use this method as a fallback to + // exit THINKING mode. + tryExitThinkingState(); } void DialogUXStateAggregator::notifyObserversOfState() { @@ -240,7 +250,7 @@ void DialogUXStateAggregator::transitionFromThinkingTimedOut() { m_executor.submit([this]() { if (DialogUXStateObserverInterface::DialogUXState::THINKING == m_currentState) { ACSDK_DEBUG(LX("transitionFromThinkingTimedOut")); - setState(DialogUXStateObserverInterface::DialogUXState::IDLE); + executeSetState(DialogUXStateObserverInterface::DialogUXState::IDLE); submitMetric(m_metricRecorder, THINKING_TIMEOUT_EXPIRES); } @@ -251,7 +261,7 @@ void DialogUXStateAggregator::transitionFromListeningTimedOut() { m_executor.submit([this]() { if (DialogUXStateObserverInterface::DialogUXState::LISTENING == m_currentState) { ACSDK_DEBUG(LX("transitionFromListeningTimedOut")); - setState(DialogUXStateObserverInterface::DialogUXState::IDLE); + executeSetState(DialogUXStateObserverInterface::DialogUXState::IDLE); submitMetric(m_metricRecorder, LISTENING_TIMEOUT_EXPIRES); } @@ -264,21 +274,45 @@ void DialogUXStateAggregator::tryEnterIdleStateOnTimer() { m_audioInputProcessorState == AudioInputProcessorObserverInterface::State::IDLE && (m_speechSynthesizerState == SpeechSynthesizerObserverInterface::SpeechSynthesizerState::FINISHED || m_speechSynthesizerState == SpeechSynthesizerObserverInterface::SpeechSynthesizerState::INTERRUPTED)) { - setState(sdkInterfaces::DialogUXStateObserverInterface::DialogUXState::IDLE); + executeSetState(sdkInterfaces::DialogUXStateObserverInterface::DialogUXState::IDLE); } }); } -void DialogUXStateAggregator::setState(sdkInterfaces::DialogUXStateObserverInterface::DialogUXState newState) { +bool DialogUXStateAggregator::executeSetState(sdkInterfaces::DialogUXStateObserverInterface::DialogUXState newState) { + bool validTransition = true; + if (newState == m_currentState) { - return; + validTransition = false; + } else { + switch (m_currentState) { + case DialogUXStateObserverInterface::DialogUXState::THINKING: + if (DialogUXStateObserverInterface::DialogUXState::LISTENING == newState) { + validTransition = false; + } + + break; + default: + break; + } } + + ACSDK_DEBUG0(LX(__func__) + .d("from", m_currentState) + .d("to", newState) + .d("validTransition", validTransition ? "true" : "false")); + + if (!validTransition) { + return false; + } + m_listeningTimeoutTimer.stop(); m_thinkingTimeoutTimer.stop(); m_multiturnSpeakingToListeningTimer.stop(); - ACSDK_DEBUG(LX("setState").d("from", m_currentState).d("to", newState)); m_currentState = newState; notifyObserversOfState(); + + return true; } void DialogUXStateAggregator::tryEnterIdleState() { @@ -286,7 +320,8 @@ void DialogUXStateAggregator::tryEnterIdleState() { m_thinkingTimeoutTimer.stop(); m_multiturnSpeakingToListeningTimer.stop(); if (!m_multiturnSpeakingToListeningTimer - .start(SHORT_TIMEOUT, std::bind(&DialogUXStateAggregator::tryEnterIdleStateOnTimer, this)) + .start( + m_shortTimeoutForThinkingToIdle, std::bind(&DialogUXStateAggregator::tryEnterIdleStateOnTimer, this)) .valid()) { ACSDK_ERROR(LX("failedToStartTryEnterIdleStateTimer")); } diff --git a/AVSCommon/AVS/src/ExceptionEncounteredSender.cpp b/AVSCommon/AVS/src/ExceptionEncounteredSender.cpp index 9779638fc2..1345122796 100644 --- a/AVSCommon/AVS/src/ExceptionEncounteredSender.cpp +++ b/AVSCommon/AVS/src/ExceptionEncounteredSender.cpp @@ -59,6 +59,12 @@ static const char ERROR_TYPE_KEY[] = "type"; /// JSON key for the ExceptionEncountered event's error message. static const char ERROR_MESSAGE_KEY[] = "message"; +std::shared_ptr ExceptionEncounteredSender:: + createExceptionEncounteredSenderInterface( + const std::shared_ptr& messageSender) { + return create(messageSender); +} + std::unique_ptr ExceptionEncounteredSender::create( std::shared_ptr messagesender) { if (!messagesender) { diff --git a/AVSCommon/AVS/src/Initialization/InitializationParametersBuilder.cpp b/AVSCommon/AVS/src/Initialization/InitializationParametersBuilder.cpp new file mode 100644 index 0000000000..41ce5c9ba7 --- /dev/null +++ b/AVSCommon/AVS/src/Initialization/InitializationParametersBuilder.cpp @@ -0,0 +1,58 @@ +/* + * 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/Initialization/InitializationParametersBuilder.h" +#include "AVSCommon/Utils/Timing/TimerDelegateFactory.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { +namespace initialization { + +std::unique_ptr InitializationParametersBuilder::create() { + return std::unique_ptr(new InitializationParametersBuilder()); +} + +InitializationParametersBuilder::InitializationParametersBuilder() { + m_initParams.timerDelegateFactory = std::make_shared(); +} + +InitializationParametersBuilder& InitializationParametersBuilder::withJsonStreams( + const std::shared_ptr>>& jsonStreams) { + m_initParams.jsonStreams = jsonStreams; + return *this; +} + +InitializationParametersBuilder& InitializationParametersBuilder::withPowerResourceManager( + const std::shared_ptr& powerResourceManager) { + m_initParams.powerResourceManager = powerResourceManager; + return *this; +} + +InitializationParametersBuilder& InitializationParametersBuilder::withTimerDelegateFactory( + const std::shared_ptr& timerDelegateFactory) { + m_initParams.timerDelegateFactory = timerDelegateFactory; + return *this; +} + +std::unique_ptr InitializationParametersBuilder::build() { + std::unique_ptr initParams(new InitializationParameters(m_initParams)); + return initParams; +} + +} // namespace initialization +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/AVS/src/Initialization/SDKPrimitivesProvider.cpp b/AVSCommon/AVS/src/Initialization/SDKPrimitivesProvider.cpp new file mode 100644 index 0000000000..9fb1a07437 --- /dev/null +++ b/AVSCommon/AVS/src/Initialization/SDKPrimitivesProvider.cpp @@ -0,0 +1,127 @@ +/* + * 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/Initialization/SDKPrimitivesProvider.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { +namespace initialization { + +/// String to identify log entries originating from this file. +static const std::string TAG("SDKPrimitivesProvider"); + +std::shared_ptr SDKPrimitivesProvider::m_provider; +std::mutex SDKPrimitivesProvider::m_mutex; + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param The event string for this @c LogEntry. + */ +#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) + +std::shared_ptr SDKPrimitivesProvider::getInstance() { + ACSDK_DEBUG5(LX(__func__)); + + std::lock_guard lock(m_mutex); + if (!m_provider) { + SDKPrimitivesProvider::m_provider = std::shared_ptr(new SDKPrimitivesProvider()); + } + + return m_provider; +} + +SDKPrimitivesProvider::SDKPrimitivesProvider() : + m_initialized{false}, + m_timerDelegateFactory{std::make_shared()} { +} + +bool SDKPrimitivesProvider::withTimerDelegateFactory( + std::shared_ptr timerDelegateFactory) { + ACSDK_DEBUG5(LX(__func__)); + + if (!timerDelegateFactory) { + ACSDK_ERROR(LX(__func__).d("reason", "nullTimerDelegateFactory")); + return false; + } + + if (isInitialized()) { + ACSDK_ERROR(LX(__func__).d("reason", "alreadyInitialized")); + return false; + } + + std::lock_guard lock(m_mutex); + m_timerDelegateFactory = timerDelegateFactory; + + return true; +} + +std::shared_ptr SDKPrimitivesProvider::getTimerDelegateFactory() { + ACSDK_DEBUG5(LX(__func__)); + + if (!isInitialized()) { + ACSDK_ERROR(LX(__func__).d("reason", "notInitialized")); + return nullptr; + } + + std::lock_guard lock(m_mutex); + return m_timerDelegateFactory; +} + +bool SDKPrimitivesProvider::initialize() { + ACSDK_DEBUG5(LX(__func__)); + + if (isInitialized()) { + ACSDK_ERROR(LX(__func__).d("reason", "alreadyInitialized")); + return false; + } + + std::lock_guard lock(m_mutex); + m_initialized = true; + + return true; +} + +bool SDKPrimitivesProvider::isInitialized() const { + ACSDK_DEBUG5(LX(__func__)); + + std::lock_guard lock(m_mutex); + return m_initialized; +} + +void SDKPrimitivesProvider::terminate() { + ACSDK_DEBUG5(LX(__func__)); + + reset(); + + std::lock_guard lock(m_mutex); + m_provider.reset(); +} + +void SDKPrimitivesProvider::reset() { + ACSDK_DEBUG5(LX(__func__)); + + std::lock_guard lock(m_mutex); + m_timerDelegateFactory.reset(); + m_initialized = false; +} + +} // namespace initialization +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/AVS/test/AlexaClientSDKInitTest.cpp b/AVSCommon/AVS/test/AlexaClientSDKInitTest.cpp index 11c8d6b898..fdd80a402e 100644 --- a/AVSCommon/AVS/test/AlexaClientSDKInitTest.cpp +++ b/AVSCommon/AVS/test/AlexaClientSDKInitTest.cpp @@ -26,6 +26,8 @@ namespace avs { namespace initialization { namespace test { +static std::vector> EMPTY_JSON_STREAMS; + using namespace std; /** @@ -34,7 +36,7 @@ using namespace std; * @note This test also validates whether libcurl supports HTTP2. */ TEST(AlexaClientSDKInitTest, test_initializeNoJSONConfig) { - ASSERT_TRUE(AlexaClientSDKInit::initialize({})); + ASSERT_TRUE(AlexaClientSDKInit::initialize(EMPTY_JSON_STREAMS)); AlexaClientSDKInit::uninitialize(); } @@ -72,7 +74,7 @@ TEST(AlexaClientSDKInitTest, test_uninitializedIsInitialized) { * Tests @c isInitialized when the SDK is initialized, expecting it to return @c true. */ TEST(AlexaClientSDKInitTest, test_isInitialized) { - ASSERT_TRUE(AlexaClientSDKInit::initialize({})); + ASSERT_TRUE(AlexaClientSDKInit::initialize(EMPTY_JSON_STREAMS)); // Expect used to ensure we uninitialize. EXPECT_TRUE(AlexaClientSDKInit::isInitialized()); AlexaClientSDKInit::uninitialize(); diff --git a/AVSCommon/AVS/test/CapabilitySemanticsTest.cpp b/AVSCommon/AVS/test/CapabilitySemanticsTest.cpp new file mode 100644 index 0000000000..0425bc28d1 --- /dev/null +++ b/AVSCommon/AVS/test/CapabilitySemanticsTest.cpp @@ -0,0 +1,443 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace avsCommon { +namespace avs { +namespace test { + +using namespace ::testing; +using namespace avsCommon::avs::capabilitySemantics; + +/// Accepted action IDs. +/// @{ +static std::string ACTION_OPEN = "Alexa.Actions.Open"; +static std::string ACTION_CLOSE = "Alexa.Actions.Close"; +static std::string ACTION_RAISE = "Alexa.Actions.Raise"; +static std::string ACTION_LOWER = "Alexa.Actions.Lower"; +/// @} + +/// Accepted state IDs. +/// @{ +static std::string STATE_OPEN = "Alexa.States.Open"; +static std::string STATE_CLOSED = "Alexa.States.Closed"; +/// @} + +/// Sample directive names. +/// @{ +static std::string DIRECTIVE_TURNOFF = "TurnOff"; +static std::string DIRECTIVE_SETMODE = "SetMode"; +static std::string DIRECTIVE_SETRANGE = "SetRangeValue"; +static std::string DIRECTIVE_ADJUSTRANGE = "AdjustRangeValue"; +/// @} + +// clang-format off + +/** + * Sample 'semantics' object with multiple ActionsToDirectiveMappings with multiple actions. + */ +static std::string JSON_SEMANTICS_MULTIPLE_ACTIONS = R"({ +"actionMappings": [ + { + "@type": "ActionsToDirective", + "actions": ["Alexa.Actions.Close", "Alexa.Actions.Lower"], + "directive": { + "name": "SetMode", + "payload": { + "mode": "Position.Down" + } + } + }, + { + "@type": "ActionsToDirective", + "actions": ["Alexa.Actions.Open", "Alexa.Actions.Raise"], + "directive": { + "name": "SetMode", + "payload": { + "mode": "Position.Up" + } + } + } +] +})"; + +/** + * Sample 'semantics' object with 'actionMappings' and 'stateMappings'. + */ +static std::string JSON_SEMANTICS_COMPLETE = R"({ +"actionMappings": [ + { + "@type": "ActionsToDirective", + "actions": [ + "Alexa.Actions.Close" + ], + "directive": { + "name": "SetRangeValue", + "payload": { + "rangeValue": 0 + } + } + }, + { + "@type": "ActionsToDirective", + "actions": [ + "Alexa.Actions.Open" + ], + "directive": { + "name": "SetRangeValue", + "payload": { + "rangeValue": 100 + } + } + }, + { + "@type": "ActionsToDirective", + "actions": [ + "Alexa.Actions.Lower" + ], + "directive": { + "name": "AdjustRangeValue", + "payload": { + "rangeValueDelta": -10 + } + } + }, + { + "@type": "ActionsToDirective", + "actions": [ + "Alexa.Actions.Raise" + ], + "directive": { + "name": "AdjustRangeValue", + "payload": { + "rangeValueDelta": 10 + } + } + } +], +"stateMappings": [ + { + "@type": "StatesToValue", + "states": [ + "Alexa.States.Closed" + ], + "value": 0 + }, + { + "@type": "StatesToRange", + "states": [ + "Alexa.States.Open" + ], + "range": { + "minimumValue": 1, + "maximumValue": 100 + } + } +] +})"; + +/// Empty JSON object. +static std::string JSON_EMPTY_OBJECT = "{}"; + +// clang-format on + +/** + * Expects the provided JSON strings to be equal. + */ +void validateJson(const std::string& providedJson, const std::string& expectedJson) { + rapidjson::Document providedStateParsed; + providedStateParsed.Parse(providedJson); + + rapidjson::Document expectedStateParsed; + expectedStateParsed.Parse(expectedJson); + + EXPECT_EQ(providedStateParsed, expectedStateParsed); +} + +/** + * The test harness for @c CapabilitySemantics. + */ +class CapabilitySemanticsTest : public Test {}; + +/** + * Test that ActionsToDirectiveMapping::addAction() checks for an empty action. + */ +TEST_F(CapabilitySemanticsTest, test_actions_emptyAction) { + ActionsToDirectiveMapping invalidMapping; + ASSERT_FALSE(invalidMapping.addAction("")); + ASSERT_FALSE(invalidMapping.isValid()); + ASSERT_EQ(invalidMapping.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that ActionsToDirectiveMapping::setDirective() checks for an empty name. + */ +TEST_F(CapabilitySemanticsTest, test_actions_emptyDirectiveName) { + ActionsToDirectiveMapping invalidMapping; + ASSERT_FALSE(invalidMapping.setDirective("", JSON_EMPTY_OBJECT)); + ASSERT_FALSE(invalidMapping.isValid()); + ASSERT_EQ(invalidMapping.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that ActionsToDirectiveMapping::addAction() skips duplicate actions. + */ +TEST_F(CapabilitySemanticsTest, test_actions_duplicateAction) { + ActionsToDirectiveMapping invalidMapping; + ASSERT_TRUE(invalidMapping.setDirective(DIRECTIVE_TURNOFF, JSON_EMPTY_OBJECT)); + ASSERT_TRUE(invalidMapping.addAction(ACTION_CLOSE)); + ASSERT_TRUE(invalidMapping.isValid()); + ASSERT_FALSE(invalidMapping.addAction(ACTION_CLOSE)); + ASSERT_FALSE(invalidMapping.isValid()); + ASSERT_EQ(invalidMapping.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that ActionsToDirectiveMapping without actions is invalid. + */ +TEST_F(CapabilitySemanticsTest, test_actions_noActions) { + ActionsToDirectiveMapping invalidMapping; + ASSERT_TRUE(invalidMapping.setDirective(DIRECTIVE_TURNOFF, JSON_EMPTY_OBJECT)); + ASSERT_FALSE(invalidMapping.isValid()); + ASSERT_EQ(invalidMapping.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that ActionsToDirectiveMapping without a directive is invalid. + */ +TEST_F(CapabilitySemanticsTest, test_actions_noDirective) { + ActionsToDirectiveMapping invalidMapping; + ASSERT_TRUE(invalidMapping.addAction(ACTION_CLOSE)); + ASSERT_FALSE(invalidMapping.isValid()); + ASSERT_EQ(invalidMapping.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that StatesToValueMapping::addState() checks for an empty state. + */ +TEST_F(CapabilitySemanticsTest, test_statesValue_emptyState) { + StatesToValueMapping invalidMapping; + ASSERT_FALSE(invalidMapping.addState("")); + ASSERT_FALSE(invalidMapping.isValid()); + ASSERT_EQ(invalidMapping.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that StatesToValueMapping::addState() skips duplicate states. + */ +TEST_F(CapabilitySemanticsTest, test_statesValue_duplicateState) { + StatesToValueMapping invalidMapping; + ASSERT_TRUE(invalidMapping.setValue("Position.Down")); + ASSERT_TRUE(invalidMapping.addState(STATE_CLOSED)); + ASSERT_TRUE(invalidMapping.isValid()); + ASSERT_FALSE(invalidMapping.addState(STATE_CLOSED)); + ASSERT_FALSE(invalidMapping.isValid()); + ASSERT_EQ(invalidMapping.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that StatesToValueMapping without states is invalid. + */ +TEST_F(CapabilitySemanticsTest, test_statesValue_noStates) { + StatesToValueMapping invalidMapping; + ASSERT_TRUE(invalidMapping.setValue(0)); + ASSERT_FALSE(invalidMapping.isValid()); + ASSERT_EQ(invalidMapping.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that StatesToValueMapping without a value is invalid. + */ +TEST_F(CapabilitySemanticsTest, test_statesValue_noValue) { + StatesToValueMapping invalidMapping; + ASSERT_TRUE(invalidMapping.addState(STATE_CLOSED)); + ASSERT_FALSE(invalidMapping.isValid()); + ASSERT_EQ(invalidMapping.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that StatesToRangeMapping::addState() checks for an empty state. + */ +TEST_F(CapabilitySemanticsTest, test_statesRange_emptyState) { + StatesToRangeMapping invalidMapping; + ASSERT_FALSE(invalidMapping.addState("")); + ASSERT_FALSE(invalidMapping.isValid()); + ASSERT_EQ(invalidMapping.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that StatesToRangeMapping::addState() skips duplicate states. + */ +TEST_F(CapabilitySemanticsTest, test_statesRange_duplicateState) { + StatesToRangeMapping invalidMapping; + ASSERT_TRUE(invalidMapping.setRange(0, 50)); + ASSERT_TRUE(invalidMapping.addState(STATE_OPEN)); + ASSERT_TRUE(invalidMapping.isValid()); + ASSERT_FALSE(invalidMapping.addState(STATE_OPEN)); + ASSERT_FALSE(invalidMapping.isValid()); + ASSERT_EQ(invalidMapping.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that StatesToRangeMapping without states is invalid. + */ +TEST_F(CapabilitySemanticsTest, test_statesRange_noStates) { + StatesToRangeMapping invalidMapping; + ASSERT_TRUE(invalidMapping.setRange(0, 50)); + ASSERT_FALSE(invalidMapping.isValid()); + ASSERT_EQ(invalidMapping.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that StatesToRangeMapping without a range is invalid. + */ +TEST_F(CapabilitySemanticsTest, test_statesRange_noRange) { + StatesToRangeMapping invalidMapping; + ASSERT_TRUE(invalidMapping.addState(STATE_CLOSED)); + ASSERT_FALSE(invalidMapping.isValid()); + ASSERT_EQ(invalidMapping.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that StatesToRangeMapping with min > max is invalid. + */ +TEST_F(CapabilitySemanticsTest, test_statesRange_invertedRange) { + StatesToRangeMapping invalidMapping; + ASSERT_TRUE(invalidMapping.addState(STATE_OPEN)); + ASSERT_FALSE(invalidMapping.setRange(100, 1)); + ASSERT_FALSE(invalidMapping.isValid()); + ASSERT_EQ(invalidMapping.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that CapabilitySemantics without mappings is invalid. + */ +TEST_F(CapabilitySemanticsTest, test_semantics_noMappings) { + CapabilitySemantics invalidSemantics; + ASSERT_FALSE(invalidSemantics.isValid()); + validateJson(invalidSemantics.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that CapabilitySemantics with an invalid ActionsToDirectiveMapping is invalid. + */ +TEST_F(CapabilitySemanticsTest, test_semantics_invalidActionsDirectiveMapping) { + ActionsToDirectiveMapping invalidMapping; + CapabilitySemantics invalidSemantics; + ASSERT_FALSE(invalidSemantics.addActionsToDirectiveMapping(invalidMapping)); + ASSERT_FALSE(invalidSemantics.isValid()); + validateJson(invalidSemantics.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that CapabilitySemantics with an invalid StatesToValueMapping is invalid. + */ +TEST_F(CapabilitySemanticsTest, test_semantics_invalidStatesValueMapping) { + StatesToValueMapping invalidMapping; + CapabilitySemantics invalidSemantics; + ASSERT_FALSE(invalidSemantics.addStatesToValueMapping(invalidMapping)); + ASSERT_FALSE(invalidSemantics.isValid()); + validateJson(invalidSemantics.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test that CapabilitySemantics with an invalid StatesToRangeMapping is invalid. + */ +TEST_F(CapabilitySemanticsTest, test_semantics_invalidStatesRangeMapping) { + StatesToRangeMapping invalidMapping; + CapabilitySemantics invalidSemantics; + ASSERT_FALSE(invalidSemantics.addStatesToRangeMapping(invalidMapping)); + ASSERT_FALSE(invalidSemantics.isValid()); + validateJson(invalidSemantics.toJson(), JSON_EMPTY_OBJECT); +} + +/** + * Test the JSON result of an ActionsToDirectiveMapping with multiple actions. + */ +TEST_F(CapabilitySemanticsTest, test_validateJson_semanticsMultipleActionMappings) { + ActionsToDirectiveMapping setModeDownMapping; + ASSERT_TRUE(setModeDownMapping.addAction(ACTION_CLOSE)); + ASSERT_TRUE(setModeDownMapping.addAction(ACTION_LOWER)); + ASSERT_TRUE(setModeDownMapping.setDirective(DIRECTIVE_SETMODE, "{\"mode\": \"Position.Down\"}")); + ASSERT_TRUE(setModeDownMapping.isValid()); + + ActionsToDirectiveMapping setModeUpMapping; + ASSERT_TRUE(setModeUpMapping.addAction(ACTION_OPEN)); + ASSERT_TRUE(setModeUpMapping.addAction(ACTION_RAISE)); + ASSERT_TRUE(setModeUpMapping.setDirective(DIRECTIVE_SETMODE, "{\"mode\": \"Position.Up\"}")); + ASSERT_TRUE(setModeUpMapping.isValid()); + + CapabilitySemantics semantics; + ASSERT_TRUE(semantics.addActionsToDirectiveMapping(setModeDownMapping)); + ASSERT_TRUE(semantics.addActionsToDirectiveMapping(setModeUpMapping)); + ASSERT_TRUE(semantics.isValid()); + validateJson(semantics.toJson(), JSON_SEMANTICS_MULTIPLE_ACTIONS); +} + +/** + * Test the JSON result of a CapabilitySemantics with all mapping types. + */ +TEST_F(CapabilitySemanticsTest, test_validateJson_semanticsComplete) { + ActionsToDirectiveMapping closeMapping; + ASSERT_TRUE(closeMapping.addAction(ACTION_CLOSE)); + ASSERT_TRUE(closeMapping.setDirective(DIRECTIVE_SETRANGE, "{\"rangeValue\" : 0}")); + ASSERT_TRUE(closeMapping.isValid()); + + ActionsToDirectiveMapping openMapping; + ASSERT_TRUE(openMapping.addAction(ACTION_OPEN)); + ASSERT_TRUE(openMapping.setDirective(DIRECTIVE_SETRANGE, "{\"rangeValue\" : 100}")); + ASSERT_TRUE(openMapping.isValid()); + + ActionsToDirectiveMapping lowerMapping; + ASSERT_TRUE(lowerMapping.addAction(ACTION_LOWER)); + ASSERT_TRUE(lowerMapping.setDirective(DIRECTIVE_ADJUSTRANGE, "{\"rangeValueDelta\" : -10}")); + ASSERT_TRUE(lowerMapping.isValid()); + + ActionsToDirectiveMapping raiseMapping; + ASSERT_TRUE(raiseMapping.addAction(ACTION_RAISE)); + ASSERT_TRUE(raiseMapping.setDirective(DIRECTIVE_ADJUSTRANGE, "{\"rangeValueDelta\" : 10}")); + ASSERT_TRUE(raiseMapping.isValid()); + + StatesToValueMapping closedMapping; + ASSERT_TRUE(closedMapping.addState(STATE_CLOSED)); + ASSERT_TRUE(closedMapping.setValue(0)); + ASSERT_TRUE(closedMapping.isValid()); + + StatesToRangeMapping openedMapping; + ASSERT_TRUE(openedMapping.addState(STATE_OPEN)); + ASSERT_TRUE(openedMapping.setRange(1, 100)); + ASSERT_TRUE(openedMapping.isValid()); + + CapabilitySemantics semantics; + ASSERT_TRUE(semantics.addActionsToDirectiveMapping(closeMapping)); + ASSERT_TRUE(semantics.addActionsToDirectiveMapping(openMapping)); + ASSERT_TRUE(semantics.addActionsToDirectiveMapping(lowerMapping)); + ASSERT_TRUE(semantics.addActionsToDirectiveMapping(raiseMapping)); + ASSERT_TRUE(semantics.addStatesToValueMapping(closedMapping)); + ASSERT_TRUE(semantics.addStatesToRangeMapping(openedMapping)); + + ASSERT_TRUE(semantics.isValid()); + validateJson(semantics.toJson(), JSON_SEMANTICS_COMPLETE); +} + +} // namespace test +} // namespace avs +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/AVS/test/DialogUXStateAggregatorTest.cpp b/AVSCommon/AVS/test/DialogUXStateAggregatorTest.cpp index c6d065d305..3d4ec15f55 100644 --- a/AVSCommon/AVS/test/DialogUXStateAggregatorTest.cpp +++ b/AVSCommon/AVS/test/DialogUXStateAggregatorTest.cpp @@ -497,6 +497,38 @@ TEST_F(DialogUXAggregatorTest, test_validStatesForRPSToThinking) { assertNoStateChange(m_testObserver); } +/// Test that if receive() happens before 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::IDLE); +} + +/// Test that if AIP experiences latency and sends a callback after RPS, we don't transition +/// LISTENING->THINKING->LISTENING. +TEST_F(DialogUXAggregatorTest, test_receiveAIPBusyAfterRPS) { + assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::IDLE); + + m_aggregator->onStateChanged(AudioInputProcessorObserverInterface::State::RECOGNIZING); + assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::LISTENING); + + m_aggregator->onRequestProcessingStarted(); + assertStateChange(m_testObserver, DialogUXStateObserverInterface::DialogUXState::THINKING); + + m_aggregator->onStateChanged(AudioInputProcessorObserverInterface::State::BUSY); + assertNoStateChange(m_testObserver); +} + } // namespace test } // namespace avsCommon } // namespace alexaClientSDK diff --git a/AVSCommon/CMakeLists.txt b/AVSCommon/CMakeLists.txt index e479ffc8a4..240dc34a67 100644 --- a/AVSCommon/CMakeLists.txt +++ b/AVSCommon/CMakeLists.txt @@ -13,7 +13,6 @@ add_library(AVSCommon SHARED AVS/src/AVSMessageHeader.cpp AVS/src/AbstractAVSConnectionManager.cpp AVS/src/BlockingPolicy.cpp - AVS/src/ExternalMediaPlayer/AdapterUtils.cpp AVS/src/AlexaClientSDKInit.cpp AVS/src/Attachment/Attachment.cpp AVS/src/Attachment/AttachmentManager.cpp @@ -30,6 +29,12 @@ add_library(AVSCommon SHARED AVS/src/EventBuilder.cpp AVS/src/ExceptionEncounteredSender.cpp AVS/src/CapabilityResources.cpp + AVS/src/CapabilitySemantics/ActionsToDirectiveMapping.cpp + AVS/src/CapabilitySemantics/CapabilitySemantics.cpp + AVS/src/CapabilitySemantics/StatesToRangeMapping.cpp + AVS/src/CapabilitySemantics/StatesToValueMapping.cpp + AVS/src/Initialization/InitializationParametersBuilder.cpp + AVS/src/Initialization/SDKPrimitivesProvider.cpp AVS/src/HandlerAndPolicy.cpp AVS/src/MessageRequest.cpp AVS/src/NamespaceAndName.cpp @@ -72,6 +77,7 @@ add_library(AVSCommon SHARED Utils/src/Logger/ThreadMoniker.cpp Utils/src/MacAddressString.cpp Utils/src/MediaPlayer/PooledMediaPlayerFactory.cpp + Utils/src/MediaPlayer/PooledMediaResourceProvider.cpp Utils/src/MediaPlayer/PlaybackContext.cpp Utils/src/Metrics.cpp Utils/src/Metrics/DataPoint.cpp @@ -80,8 +86,13 @@ add_library(AVSCommon SHARED Utils/src/Metrics/DataPointStringBuilder.cpp Utils/src/Metrics/MetricEvent.cpp Utils/src/Metrics/MetricEventBuilder.cpp + Utils/src/Metrics/UplData.cpp Utils/src/MultiTimer.cpp Utils/src/Network/InternetConnectionMonitor.cpp + Utils/src/Power/AggregatedPowerResourceManager.cpp + Utils/src/Power/PowerMonitor.cpp + Utils/src/Power/PowerResource.cpp + Utils/src/Power/WakeGuard.cpp Utils/src/RequiresShutdown.cpp Utils/src/RetryTimer.cpp Utils/src/SafeCTimeAccess.cpp @@ -92,11 +103,15 @@ add_library(AVSCommon SHARED Utils/src/SystemClockMonitor.cpp Utils/src/TaskThread.cpp Utils/src/ThreadPool.cpp + Utils/src/Threading/ConditionVariableWrapper.cpp Utils/src/TimePoint.cpp Utils/src/TimeUtils.cpp Utils/src/Timer.cpp + Utils/src/Timing/TimerDelegate.cpp + Utils/src/Timing/TimerDelegateFactory.cpp Utils/src/UUIDGeneration.cpp Utils/src/WaitEvent.cpp + Utils/src/WavUtils.cpp Utils/src/WorkerThread.cpp) target_include_directories(AVSCommon PUBLIC @@ -122,7 +137,9 @@ target_link_libraries(AVSCommon ${CUSTOMSDSTRAITS_LIB_NAME}) endif () target_link_libraries(AVSCommon - ${CURL_LIBRARIES}) + ${CURL_LIBRARIES} + acsdkEqualizerInterfaces + acsdkApplicationAudioPipelineFactoryInterfaces) # install target LIST(APPEND PATHS "${PROJECT_SOURCE_DIR}/AVS/include") diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AVSConnectionManagerInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AVSConnectionManagerInterface.h index 3ac7c4890e..bbf39c2e19 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AVSConnectionManagerInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AVSConnectionManagerInterface.h @@ -18,8 +18,10 @@ #include +#include "AVSCommon/SDKInterfaces/AVSGatewayAssignerInterface.h" #include "AVSCommon/SDKInterfaces/ConnectionStatusObserverInterface.h" #include "AVSCommon/SDKInterfaces/MessageObserverInterface.h" +#include "AVSCommon/SDKInterfaces/MessageSenderInterface.h" namespace alexaClientSDK { namespace avsCommon { @@ -28,7 +30,9 @@ namespace sdkInterfaces { /** * This class reflects a connection to AVS and how it may be observed. */ -class AVSConnectionManagerInterface { +class AVSConnectionManagerInterface + : public MessageSenderInterface + , public AVSGatewayAssignerInterface { public: /// Type alias for brevity. using ConnectionStatusObserverInterface = avsCommon::sdkInterfaces::ConnectionStatusObserverInterface; diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AVSGatewayAssignerInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AVSGatewayAssignerInterface.h index bb12d48f1e..83a205ca69 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AVSGatewayAssignerInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AVSGatewayAssignerInterface.h @@ -36,6 +36,13 @@ class AVSGatewayAssignerInterface { * @param avsGateway AVS gateway to set. */ virtual void setAVSGateway(const std::string& avsGateway) = 0; + + /** + * Gets the current gateway URL for AVS connection. + * + * @return The current gateway URL for AVS connection. + */ + virtual std::string getAVSGateway() const = 0; }; } // namespace sdkInterfaces diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AlexaEventProcessedObserverInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AlexaEventProcessedObserverInterface.h index 4eb62b6776..8e1087cee1 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AlexaEventProcessedObserverInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AlexaEventProcessedObserverInterface.h @@ -16,6 +16,8 @@ #ifndef ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_ALEXAEVENTPROCESSEDOBSERVERINTERFACE_H_ #define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_ALEXAEVENTPROCESSEDOBSERVERINTERFACE_H_ +#include + namespace alexaClientSDK { namespace avsCommon { namespace sdkInterfaces { diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ApplicationMediaInterfaces.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ApplicationMediaInterfaces.h index bf870e637a..e157b295c6 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ApplicationMediaInterfaces.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ApplicationMediaInterfaces.h @@ -16,7 +16,8 @@ #ifndef ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_APPLICATIONMEDIAINTERFACES_H_ #define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_APPLICATIONMEDIAINTERFACES_H_ -#include +#include +#include #include #include #include @@ -36,12 +37,14 @@ struct ApplicationMediaInterfaces { * @param speaker Speaker * @param equalizer Equalizer * @param requiresShutdown Components requiring shutdown + * @param channelVolume Channel volume interface. Optional; default is a nullptr. */ ApplicationMediaInterfaces( std::shared_ptr mediaPlayer, std::shared_ptr speaker, - std::shared_ptr equalizer, - std::shared_ptr requiresShutdown); + std::shared_ptr equalizer, + std::shared_ptr requiresShutdown, + std::shared_ptr channelVolume = nullptr); /// Media Player std::shared_ptr mediaPlayer; @@ -50,21 +53,26 @@ struct ApplicationMediaInterfaces { std::shared_ptr speaker; /// Equalizer implementation - std::shared_ptr equalizer; + std::shared_ptr equalizer; /// Requires Shutdown implementation std::shared_ptr requiresShutdown; + + /// ChannelVolume implementation + std::shared_ptr channelVolume; }; inline ApplicationMediaInterfaces::ApplicationMediaInterfaces( std::shared_ptr mediaPlayer, std::shared_ptr speaker, - std::shared_ptr equalizer, - std::shared_ptr requiresShutdown) : + std::shared_ptr equalizer, + std::shared_ptr requiresShutdown, + std::shared_ptr channelVolume) : mediaPlayer{mediaPlayer}, speaker{speaker}, equalizer{equalizer}, - requiresShutdown{requiresShutdown} { + requiresShutdown{requiresShutdown}, + channelVolume{channelVolume} { } } // namespace sdkInterfaces diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AudioFocusAnnotation.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AudioFocusAnnotation.h new file mode 100644 index 0000000000..1700293ca3 --- /dev/null +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/AudioFocusAnnotation.h @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_AUDIOFOCUSANNOTATION_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_AUDIOFOCUSANNOTATION_H_ + +namespace alexaClientSDK { +namespace avsCommon { +namespace sdkInterfaces { + +/** + * Annotation for audio-related focus implementations. + */ +struct AudioFocusAnnotation {}; + +} // namespace sdkInterfaces +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_AUDIOFOCUSANNOTATION_H_ diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ConnectionStatusObserverInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ConnectionStatusObserverInterface.h index 1e6aae6085..05cf05a637 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ConnectionStatusObserverInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ConnectionStatusObserverInterface.h @@ -17,11 +17,14 @@ #define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_CONNECTIONSTATUSOBSERVERINTERFACE_H_ #include +#include namespace alexaClientSDK { namespace avsCommon { namespace sdkInterfaces { +static const int ENGINE_TYPE_ALEXA_VOICE_SERVICES = 1; + /** * This class allows a client to be notified of changes to connection status to AVS. */ @@ -97,6 +100,23 @@ class ConnectionStatusObserverInterface { SERVER_ENDPOINT_CHANGED }; + struct EngineConnectionStatus { + /** + * Constructor + */ + EngineConnectionStatus(const int connectEngineType, ChangedReason connectReason, Status connectStatus) : + engineType{connectEngineType}, + reason{connectReason}, + status{connectStatus} { + } + // The engine type + int engineType; + // Reason for connection state change + ChangedReason reason; + // State of connection + Status status; + }; + /** * Destructor. */ @@ -109,6 +129,18 @@ class ConnectionStatusObserverInterface { * @param reason The reason the status change occurred. */ virtual void onConnectionStatusChanged(const Status status, const ChangedReason reason) = 0; + + /** + * Called when any of the connection state changes. + * + * @param status A summarized status based on one or more engines' connection status. Most applications should use + * this to identify Alexa connectivity. + * @param engineStatuses Detailed status for each connection. Gives more granular connection status when more than + * one connection is possible. + */ + virtual void onConnectionStatusChanged( + const Status status, + const std::vector& engineStatuses){}; }; /** diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Diagnostics/DevicePropertyAggregatorInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Diagnostics/DevicePropertyAggregatorInterface.h index 43912da463..9c3e38c453 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Diagnostics/DevicePropertyAggregatorInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Diagnostics/DevicePropertyAggregatorInterface.h @@ -22,12 +22,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include namespace alexaClientSDK { namespace avsCommon { @@ -40,6 +42,7 @@ namespace diagnostics { class DevicePropertyAggregatorInterface : public acsdkAlertsInterfaces::AlertObserverInterface , public acsdkAudioPlayerInterfaces::AudioPlayerObserverInterface + , public avsCommon::sdkInterfaces::AuthObserverInterface , public avsCommon::sdkInterfaces::ConnectionStatusObserverInterface , public avsCommon::sdkInterfaces::ContextRequesterInterface , public acsdkNotificationsInterfaces::NotificationsObserverInterface @@ -83,6 +86,18 @@ class DevicePropertyAggregatorInterface /// "false". static constexpr const char* AVS_ALERTS_MUTE = "AVSAlertsMute"; + /// Property Key for Do not Disturb. The Property Value is a string representing if do not disturb is on. Ex: + /// "false". + static constexpr const char* DO_NOT_DISTURB = "DoNotDisturb"; + + /// Property Key for Locale. The Property Value is a string representing the locale of the device. Ex: + /// "[en-US]". + static constexpr const char* LOCALE = "Locale"; + + /// Property Key for Registration status. The Property Value is a string representing the registration status. Ex: + /// "true". + static constexpr const char* REGISTRATION_STATUS = "RegistrationStatus"; + /** * Gets the property for the given property key. * @@ -106,6 +121,12 @@ class DevicePropertyAggregatorInterface virtual void setContextManager( std::shared_ptr contextManager) = 0; + /** + * Set the @c DeviceSettingsManager + * + * @param settingManager the @c DeviceSettingsManager + */ + virtual void setDeviceSettingsManager(std::shared_ptr settingManager) = 0; /** * @note This API should only be used to initialize the volume values. Subsequent updates to volume values should * come from the @c SpeakerManagerInterface. diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Diagnostics/DiagnosticsInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Diagnostics/DiagnosticsInterface.h index 97a6608772..e7bd381ffc 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Diagnostics/DiagnosticsInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Diagnostics/DiagnosticsInterface.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -56,10 +57,12 @@ class DiagnosticsInterface { * * @param sequencer An instance of the @c DirectiveSequencerInterface. * @param attachmentManager An instance of the @c AttachmentManager. + * @param avsConnectionManager An instance of the @c AVSConnectionManager. */ virtual void setDiagnosticDependencies( std::shared_ptr sequencer, - std::shared_ptr attachmentManager) = 0; + std::shared_ptr attachmentManager, + std::shared_ptr avsConnectionManager) = 0; /** * An API to inject audio utterances into the SDK. diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/DefaultEndpointAnnotation.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/DefaultEndpointAnnotation.h new file mode 100644 index 0000000000..1e54d62c29 --- /dev/null +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/DefaultEndpointAnnotation.h @@ -0,0 +1,33 @@ +/* + * 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_ENDPOINTS_DEFAULTENDPOINTANNOTATION_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_ENDPOINTS_DEFAULTENDPOINTANNOTATION_H_ + +namespace alexaClientSDK { +namespace avsCommon { +namespace sdkInterfaces { +namespace endpoints { + +/** + * Annotation for components used to build the default endpoint. + */ +struct DefaultEndpointAnnotation {}; + +} // namespace endpoints +} // namespace sdkInterfaces +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_ENDPOINTS_DEFAULTENDPOINTANNOTATION_H_ diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/EndpointBuilderInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/EndpointBuilderInterface.h index 9ff78cca2e..d22977fa96 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/EndpointBuilderInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/EndpointBuilderInterface.h @@ -19,6 +19,10 @@ #include #include +#include "AVSCommon/SDKInterfaces/CapabilityConfigurationInterface.h" +#include "AVSCommon/SDKInterfaces/DirectiveHandlerInterface.h" +#include "AVSCommon/SDKInterfaces/Endpoints/EndpointCapabilitiesBuilderInterface.h" +#include "AVSCommon/SDKInterfaces/Endpoints/EndpointCapabilitiesRegistrarInterface.h" #include "AVSCommon/SDKInterfaces/Endpoints/EndpointInterface.h" #include "AVSCommon/SDKInterfaces/ModeController/ModeControllerAttributes.h" #include "AVSCommon/SDKInterfaces/ModeController/ModeControllerInterface.h" @@ -59,7 +63,7 @@ namespace endpoints { * build. * */ -class EndpointBuilderInterface { +class EndpointBuilderInterface : public EndpointCapabilitiesRegistrarInterface { public: /** * Destructor. @@ -196,6 +200,8 @@ class EndpointBuilderInterface { /** * Configures builder to use a @c PowerControllerInterface * + * @deprecated use the new @c withEndpointCapabilitiesBuilder() method instead. + * * @param powerController An interface that provides the power operations. * @param isProactivelyReported Whether the property state change is proactively reported. * @param isRetrievable Whether the property state is retrievable. @@ -211,6 +217,8 @@ class EndpointBuilderInterface { * * @note The builder will fail if the instance name is already used in that endpoint. * + * @deprecated use the new @c withEndpointCapabilitiesBuilder() method instead. + * * @param toggleController An interface that performs the toggle operations. * @param instance A non-empty string identifying an instance of a toggle controller uniquely in a endpoint. * @param toggleControllerAttributes The attributes used in capability discovery message. @@ -233,6 +241,8 @@ class EndpointBuilderInterface { * * @note The builder will fail if the instance name is already used in that endpoint. * + * @deprecated use the new @c withEndpointCapabilitiesBuilder() method instead. + * * @param modeController An interface that provides the mode operations. * @param instance A non-empty string identifying an instance of a mode controller uniquely in a endpoint. * @param modeControllerAttributes The attributes used in capability discovery message. @@ -255,6 +265,8 @@ class EndpointBuilderInterface { * * @note The builder will fail if the instance name is already used in that endpoint. * + * @deprecated use the new @c withEndpointCapabilitiesBuilder() method instead. + * * @param rangeController An interface that provides the range operations. * @param instance A non-empty string identifying an instance of a range controller uniquely in a endpoint. * @param rangeControllerAttributes The attributes used in capability discovery message. @@ -272,6 +284,19 @@ class EndpointBuilderInterface { bool isRetrievable, bool isNonControllable = false) = 0; + /** + * Configures builder to use a @c EndpointCapabilitiesBuilder object that can be used to build multiple capability + * agents. + * + * @note The builder will fail if the endpointCapabilitiesBuilder fails in generating valid capability agents. + * + * @param endpointCapabilitiesBuilder A @c EndpointCapabilitiesBuilder object. + * @return This builder which can be used to nest configuration function calls. + */ + virtual EndpointBuilderInterface& withEndpointCapabilitiesBuilder( + const std::shared_ptr& + endpointCapabilitiesBuilder) = 0; + /** * Builds an endpoint with the configured properties / components. * diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/EndpointCapabilitiesBuilderInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/EndpointCapabilitiesBuilderInterface.h new file mode 100644 index 0000000000..36f2b35955 --- /dev/null +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/EndpointCapabilitiesBuilderInterface.h @@ -0,0 +1,81 @@ +/* + * 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_ENDPOINTS_ENDPOINTCAPABILITIESBUILDERINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_ENDPOINTS_ENDPOINTCAPABILITIESBUILDERINTERFACE_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace sdkInterfaces { +namespace endpoints { + +/** + * This class provides a mechanism through which Endpoint Capability Agents can be passed to the @c EndpointBuilder. + * + * @note The @c EndpointBuilder calls @c buildCapabilties() in withEndpointCapabilitiesBuilder() method with all + * required dependencies to build the capability agents. + * + * @note The user must ensure thread safety of this class. + */ +class EndpointCapabilitiesBuilderInterface { +public: + /** + * Helper structure containing @c CapabilityConfiguration and @c DirectiveHandler. + */ + struct Capability { + avsCommon::avs::CapabilityConfiguration configuration; + std::shared_ptr directiveHandler; + }; + + /** + * Default Destructor. + */ + virtual ~EndpointCapabilitiesBuilderInterface() = default; + + /** + * The method builds controller capabilities and returns a pair of lists containing @c Capability and lists + * containing @c RequiresShutdown objects. + * @param endpointId The endpoint ID + * @param contextManager The @c ContextManager pointer. + * @param responseSender The @c AlexaInterfaceMessageSender to send Alexa Interface messages. + * @param exceptionSender The @c ExceptionEncounteredSender to send exception messages to AVS. + * @return A pair of lists of @c Capability and @c RequiresShutdown objects + */ + virtual std::pair, std::list>> + buildCapabilities( + const avsCommon::sdkInterfaces::endpoints::EndpointIdentifier& endpointId, + const std::shared_ptr& contextManager, + const std::shared_ptr& responseSender, + const std::shared_ptr& exceptionSender) = 0; +}; + +} // namespace endpoints +} // namespace sdkInterfaces +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_ENDPOINTS_ENDPOINTCAPABILITIESBUILDERINTERFACE_H_ diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/EndpointCapabilitiesRegistrarInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/EndpointCapabilitiesRegistrarInterface.h new file mode 100644 index 0000000000..0723aafc51 --- /dev/null +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Endpoints/EndpointCapabilitiesRegistrarInterface.h @@ -0,0 +1,81 @@ +/* + * 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_ENDPOINTS_ENDPOINTCAPABILITIESREGISTRARINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_ENDPOINTS_ENDPOINTCAPABILITIESREGISTRARINTERFACE_H_ + +#include + +#include "AVSCommon/SDKInterfaces/CapabilityConfigurationInterface.h" +#include "AVSCommon/SDKInterfaces/DirectiveHandlerInterface.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace sdkInterfaces { +namespace endpoints { + +/** + * Class to register capabilities with an endpoint being built. + * + * @note Capabilities cannot be added to an endpoint that has already been built. + * + */ +class EndpointCapabilitiesRegistrarInterface { +public: + /** + * Destructor. + */ + virtual ~EndpointCapabilitiesRegistrarInterface() = default; + + /** + * Adds a capability. + * + * @param configuration The capability configuration. + * @param directiveHandler The handler for the directives in the given namespace. + * @return This registrar which can be used to nest configuration function calls. + */ + virtual EndpointCapabilitiesRegistrarInterface& withCapability( + const avs::CapabilityConfiguration& configuration, + std::shared_ptr directiveHandler) = 0; + + /** + * Adds a capability. + * + * @param configuration The object used to retrieve the capability configurations. + * @param directiveHandler The handler for the directives in the given namespace. + * @deprecated CapabilityConfigurationInterface is deprecated. + * @return This registrar which can be used to nest configuration function calls. + */ + virtual EndpointCapabilitiesRegistrarInterface& withCapability( + const std::shared_ptr& configurationInterface, + std::shared_ptr directiveHandler) = 0; + + /** + * Adds a capability configuration that doesn't have any associated @c DirectiveHandler. + * + * @param configurationInterface The object used to retrieve the capability configurations. + * @deprecated CapabilityConfigurationInterface is deprecated. + * @return This registrar which can be used to nest configuration function calls. + */ + virtual EndpointCapabilitiesRegistrarInterface& withCapabilityConfiguration( + const std::shared_ptr& configurationInterface) = 0; +}; + +} // namespace endpoints +} // namespace sdkInterfaces +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_ENDPOINTS_ENDPOINTCAPABILITIESREGISTRARINTERFACE_H_ diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ExpectSpeechTimeoutHandlerInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ExpectSpeechTimeoutHandlerInterface.h new file mode 100644 index 0000000000..72b46004ae --- /dev/null +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ExpectSpeechTimeoutHandlerInterface.h @@ -0,0 +1,65 @@ +/* + * 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_EXPECTSPEECHTIMEOUTHANDLERINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_EXPECTSPEECHTIMEOUTHANDLERINTERFACE_H_ + +#include +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace sdkInterfaces { + +/** + * Offers implementations the ability to handle the @c ExpectSpeech timeout. As an example, this may be useful to + * applications with remote microphones. + */ +class ExpectSpeechTimeoutHandlerInterface { +public: + /** + * Destructor. + */ + virtual ~ExpectSpeechTimeoutHandlerInterface() = default; + + /** + * 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. + * + * @param timeout The timeout of the @c ExpectSpeech directive. + * @param expectSpeechTimedOut An @c std::function that applications may call if the timeout expires. This results + * in an @c ExpectSpeechTimedOut event being sent to AVS if no @c recognize() call is made prior to the timeout + * expiring. This function will return a future which is @c true if called in the correct state and an + * @c ExpectSpeechTimeout Event was sent successfully, or @c false otherwise. + * @return @c true if the @c ExpectSpeech directive's timeout will be handled externally and should not be handled + * via an internal timer owned by the @c AudioInputProcessor. + * + * @note This function will be called after any calls to the @c AudioInputProcessorObserverInterface's + * onStateChanged() method to notify of a state change to @c EXPECTING_SPEECH. + * @note Implementations are not required to be thread-safe. + * @note If the @cAudioInputProcessor object that this call came from is destroyed, the @c expectSpeechTimedOut + * parameter is no longer valid. + */ + virtual bool handleExpectSpeechTimeout( + std::chrono::milliseconds timeout, + const std::function()>& expectSpeechTimedOut) = 0; +}; + +} // namespace sdkInterfaces +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_EXPECTSPEECHTIMEOUTHANDLERINTERFACE_H_ diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ExternalMediaPlayerInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ExternalMediaPlayerInterface.h deleted file mode 100644 index 33c26b3467..0000000000 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ExternalMediaPlayerInterface.h +++ /dev/null @@ -1,55 +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_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_EXTERNALMEDIAPLAYERINTERFACE_H_ -#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_EXTERNALMEDIAPLAYERINTERFACE_H_ - -#include -#include -#include -#include -#include -#include -#include - -namespace alexaClientSDK { -namespace avsCommon { -namespace sdkInterfaces { - -/** - * This class provides an interface to the @c ExternalMediaPlayer. - * Currently it provides an interface for adapters to set the player in focus when they acquire focus. - */ -class ExternalMediaPlayerInterface { -public: - /** - * Destructor - */ - virtual ~ExternalMediaPlayerInterface() = default; - - /** - * Method to set the player in focus after an adapter has acquired the channel. - * - * @param playerInFocus The business name of the adapter that has currently acquired focus. - * @note This function should not be called during the callback in @c ExternalMediaAdapterInterface. - */ - virtual void setPlayerInFocus(const std::string& playerInFocus) = 0; -}; - -} // namespace sdkInterfaces -} // namespace avsCommon -} // namespace alexaClientSDK - -#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_EXTERNALMEDIAPLAYERINTERFACE_H_ diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/HTTPContentFetcherInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/HTTPContentFetcherInterface.h index feb4556591..9b11856023 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/HTTPContentFetcherInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/HTTPContentFetcherInterface.h @@ -99,6 +99,16 @@ class HTTPContentFetcherInterface { */ virtual std::string getUrl() const = 0; + /** + * Gets the effective URL associated with this content fetcher. + * + * @note This is useful in the case of redirects where the first URL used redirects to a different URL to fetch the + * content. + * + * @return The content fetcher's effective URL. + */ + virtual std::string getEffectiveUrl() const = 0; + /** * Waits until the header was fetched successfully. If any problem happened during header, returns @c false. After * the header was already fetched, this method can be called multiple times and will return immediately. diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ModeController/ModeControllerAttributeBuilderInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ModeController/ModeControllerAttributeBuilderInterface.h index d342e771ae..f8d803008d 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ModeController/ModeControllerAttributeBuilderInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ModeController/ModeControllerAttributeBuilderInterface.h @@ -17,6 +17,7 @@ #define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_MODECONTROLLER_MODECONTROLLERATTRIBUTEBUILDERINTERFACE_H_ #include +#include #include #include "ModeControllerAttributes.h" @@ -84,6 +85,16 @@ class ModeControllerAttributeBuilderInterface { */ virtual ModeControllerAttributeBuilderInterface& setOrdered(bool ordered) = 0; + /** + * Configures the builder to use the specified semantics definition. + * @see https://developer.amazon.com/docs/alexa/alexa-voice-service/alexa-modecontroller.html + * + * @param semantics The @c CapabilitySemantics representing the semantics definition. + * @return This builder, which can be used to nest configuration function calls. + */ + virtual ModeControllerAttributeBuilderInterface& withSemantics( + const avsCommon::avs::capabilitySemantics::CapabilitySemantics& semantics) = 0; + /** * Builds a @c ModeControllerAttributes with the configured properties. * diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ModeController/ModeControllerAttributes.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ModeController/ModeControllerAttributes.h index 9eb6df2871..9ec0665a4d 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ModeController/ModeControllerAttributes.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ModeController/ModeControllerAttributes.h @@ -19,6 +19,8 @@ #include #include +#include +#include namespace alexaClientSDK { namespace avsCommon { @@ -54,6 +56,20 @@ struct ModeControllerAttributes { const std::unordered_map& modes, bool ordered); + /** + * Constructor to build the @c ModeControllerAttributes using provided values. + * + * @param capabilityResources The capability resources. + * @param modes A map of controller modes and mode resources. + * @param ordered A boolean indicating the ordering of @c modes. + * @param semantics The optional semantics definition. + */ + ModeControllerAttributes( + const avsCommon::avs::CapabilityResources& capabilityResources, + const std::unordered_map& modes, + bool ordered, + avsCommon::utils::Optional semantics); + /// A capability resource @c CapabilityResources const avsCommon::avs::CapabilityResources capabilityResources; @@ -62,6 +78,9 @@ struct ModeControllerAttributes { /// If @c true indicates that the modes in @c modes are ordered. const bool ordered; + + /// A semantics definition as an @c Optional @c CapabilitySemantics + avsCommon::utils::Optional semantics; }; inline ModeControllerAttributes::ModeControllerAttributes() : @@ -77,7 +96,19 @@ inline ModeControllerAttributes::ModeControllerAttributes( bool ordered) : capabilityResources{capabilityResources}, modes{modes}, - ordered{ordered} { + ordered{ordered}, + semantics{avsCommon::utils::Optional()} { +} + +inline ModeControllerAttributes::ModeControllerAttributes( + const avsCommon::avs::CapabilityResources& capabilityResources, + const std::unordered_map& modes, + bool ordered, + avsCommon::utils::Optional semantics) : + capabilityResources{capabilityResources}, + modes{modes}, + ordered{ordered}, + semantics{semantics} { } } // namespace modeController diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/PowerResourceManagerInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/PowerResourceManagerInterface.h index 9bedd72ec1..9d77a8f425 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/PowerResourceManagerInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/PowerResourceManagerInterface.h @@ -16,6 +16,11 @@ #ifndef ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_POWERRESOURCEMANAGERINTERFACE_H_ #define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_POWERRESOURCEMANAGERINTERFACE_H_ +#include +#include +#include +#include + namespace alexaClientSDK { namespace avsCommon { namespace sdkInterfaces { @@ -71,7 +76,7 @@ class PowerResourceManagerInterface { */ virtual void acquirePowerResource( const std::string& component, - const PowerResourceLevel level = PowerResourceLevel::ACTIVE_HIGH) = 0; + const PowerResourceLevel level = PowerResourceLevel::STANDBY_MED) = 0; /** * Release the acquired power resource of the specified component. @@ -85,8 +90,108 @@ class PowerResourceManagerInterface { * @return true if the power resource had been acquired, otherwise return false. */ virtual bool isPowerResourceAcquired(const std::string& component) = 0; + + /** + * New APIs to support refcount and acquire with timeout. + * Use the below new APIs - create(), acquire(), release() and close() + * if you need refcounting or autorelease timeout support for your component. + * @warning Do not mix and match new and legacy APIs + */ + + /** + * Class PowerResourceId used to represent a power resource + */ + + class PowerResourceId { + public: + /// Getter for the resourceId string member + /// @return resourceId string member + std::string getResourceId() const { + return m_resourceId; + } + /// Constructor + PowerResourceId(const std::string& resourceId) : m_resourceId(resourceId) { + } + + private: + /// string member denoting resourceId used to key this object + const std::string m_resourceId; + }; + + /** + * Create a power resource keyed by the unique string resourceId. + * @param resourceId mentions what the resource is for. + * @param isRefCounted whether refcounting is enabled for this resource + * @param level power resource level. + * @return shared pointer of type PowerResourceId representing the resource + */ + virtual std::shared_ptr create( + const std::string& resourceId, + bool isRefCounted = true, + const PowerResourceLevel level = PowerResourceLevel::STANDBY_MED) = 0; + + /** + * Acquire a power resource. + * @param id shared pointer of type PowerResourceId representing the resource. + * @param autoReleaseTimeout auto release timeout value. Zero denotes auto release disabled. + * @return true if acquire was successful, false if it failed. + */ + virtual bool acquire( + const std::shared_ptr& id, + const std::chrono::milliseconds autoReleaseTimeout = std::chrono::milliseconds::zero()) = 0; + + /** + * Release a power resource. + * @param id shared pointer of type PowerResourceId representing the resource. + * @return true if release was successful, false if it failed. + */ + virtual bool release(const std::shared_ptr& id) = 0; + + /** + * Close a power resource. + * @param id shared pointer of type PowerResourceId representing the resource. + * @return true if close was successful, false if it failed. + */ + virtual bool close(const std::shared_ptr& id) = 0; }; +/** + * Converts the @c PowerResourceLevel enum to a string. + * + * @param The level to convert. + * @return A string representation of the level. + */ +inline std::string powerResourceLevelToString(PowerResourceManagerInterface::PowerResourceLevel level) { + switch (level) { + case PowerResourceManagerInterface::PowerResourceLevel::STANDBY_LOW: + return "STANDBY_LOW"; + case PowerResourceManagerInterface::PowerResourceLevel::STANDBY_MED: + return "STANDBY_MED"; + case PowerResourceManagerInterface::PowerResourceLevel::STANDBY_HIGH: + return "STANDBY_HIGH"; + case PowerResourceManagerInterface::PowerResourceLevel::ACTIVE_LOW: + return "ACTIVE_LOW"; + case PowerResourceManagerInterface::PowerResourceLevel::ACTIVE_MED: + return "ACTIVE_MED"; + case PowerResourceManagerInterface::PowerResourceLevel::ACTIVE_HIGH: + return "ACTIVE_HIGH"; + } + + return "UNKNOWN"; +} + +/** + * Overload for the @c PowerResourceLevel enum. This will write the @c PowerResourceLevel as a string to the provided + * stream. + * + * @param An ostream to send the level as a string. + * @param The level to convert. + * @return The stream. + */ +inline std::ostream& operator<<(std::ostream& stream, PowerResourceManagerInterface::PowerResourceLevel level) { + return stream << powerResourceLevelToString(level); +} + } // namespace sdkInterfaces } // namespace avsCommon } // namespace alexaClientSDK diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/RangeController/RangeControllerAttributeBuilderInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/RangeController/RangeControllerAttributeBuilderInterface.h index 3dd41b1e5f..5dc7590def 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/RangeController/RangeControllerAttributeBuilderInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/RangeController/RangeControllerAttributeBuilderInterface.h @@ -17,6 +17,7 @@ #define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_RANGECONTROLLER_RANGECONTROLLERATTRIBUTEBUILDERINTERFACE_H_ #include +#include #include #include "RangeControllerAttributes.h" @@ -75,6 +76,16 @@ class RangeControllerAttributeBuilderInterface { virtual RangeControllerAttributeBuilderInterface& addPreset( const std::pair& preset) = 0; + /** + * Configures the builder to use the specified semantics definition. + * @see https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/alexa-rangecontroller.html + * + * @param semantics The @c CapabilitySemantics representing the semantics definition. + * @return This builder, which can be used to nest configuration function calls. + */ + virtual RangeControllerAttributeBuilderInterface& withSemantics( + const avsCommon::avs::capabilitySemantics::CapabilitySemantics& semantics) = 0; + /** * Builds a @c RangeControllerAttributes with the configured properties. * diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/RangeController/RangeControllerAttributes.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/RangeController/RangeControllerAttributes.h index 16f2169fe5..b342455227 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/RangeController/RangeControllerAttributes.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/RangeController/RangeControllerAttributes.h @@ -17,6 +17,7 @@ #define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_RANGECONTROLLER_RANGECONTROLLERATTRIBUTES_H_ #include +#include #include #include @@ -54,6 +55,20 @@ struct RangeControllerAttributes { const utils::Optional& unitOfMeasure, const std::vector>& presets); + /** + * Constructor to build the @c RangeControllerAttributes using provided values. + * + * @param capabilityResources The capability resources. + * @param unitOfMeasure The unit of measure of range defined using @c AlexaUnitOfMeasure + * @param presets presets of the range controller. + * @param semantics The optional semantics definition. + */ + RangeControllerAttributes( + const avsCommon::avs::CapabilityResources& capabilityResources, + const utils::Optional& unitOfMeasure, + const std::vector>& presets, + avsCommon::utils::Optional semantics); + /// A capability resource @c CapabilityResources const avsCommon::avs::CapabilityResources capabilityResources; @@ -65,6 +80,9 @@ struct RangeControllerAttributes { * PresetResources. */ std::vector> presets; + + /// A semantics definition as an @c Optional @c CapabilitySemantics + avsCommon::utils::Optional semantics; }; inline RangeControllerAttributes::RangeControllerAttributes() { @@ -76,7 +94,19 @@ inline RangeControllerAttributes::RangeControllerAttributes( const std::vector>& presets) : capabilityResources{capabilityResources}, unitOfMeasure{unitOfMeasure}, - presets{presets} { + presets{presets}, + semantics{avsCommon::utils::Optional()} { +} + +inline RangeControllerAttributes::RangeControllerAttributes( + const avsCommon::avs::CapabilityResources& capabilityResources, + const utils::Optional& unitOfMeasure, + const std::vector>& presets, + avsCommon::utils::Optional semantics) : + capabilityResources{capabilityResources}, + unitOfMeasure{unitOfMeasure}, + presets{presets}, + semantics{semantics} { } } // namespace rangeController diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/RenderPlayerInfoCardsProviderRegistrarInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/RenderPlayerInfoCardsProviderRegistrarInterface.h new file mode 100644 index 0000000000..721665149c --- /dev/null +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/RenderPlayerInfoCardsProviderRegistrarInterface.h @@ -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. + */ + +#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_RENDERPLAYERINFOCARDSPROVIDERREGISTRARINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_RENDERPLAYERINFOCARDSPROVIDERREGISTRARINTERFACE_H_ + +#include +#include + +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace sdkInterfaces { + +/** + * This class provides an interface to register providers of the @c RenderPlayerInfoCardsProviderInterface. + */ +class RenderPlayerInfoCardsProviderRegistrarInterface { +public: + /** + * Destructor + */ + virtual ~RenderPlayerInfoCardsProviderRegistrarInterface() = default; + + /** + * Add a new @c RenderPlayerInfoCardsProviderInterface instance to provide cards when creating a TemplateRuntime CA. + * + * @param provider The @c RenderPlayerInfoCardsProviderInterface instance to add. + */ + virtual bool registerProvider( + const std::shared_ptr& provider) = 0; + + /** + * Get the set of @c RenderPlayerInfoCardsProviderInterface instances to be invoked when creating a + * TemplateRuntime CA. + * + * @return A vector of @c RenderPlayerInfoCardsProviderInterface instances. + */ + virtual std::unordered_set> + getProviders() = 0; +}; + +} // namespace sdkInterfaces +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_RENDERPLAYERINFOCARDSPROVIDERREGISTRARINTERFACE_H_ diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeechInteractionHandlerInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeechInteractionHandlerInterface.h index 2b8a8dd5a8..830eadc351 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeechInteractionHandlerInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/SpeechInteractionHandlerInterface.h @@ -78,11 +78,14 @@ class SpeechInteractionHandlerInterface { * @param holdToTalkAudioProvider The audio provider containing the audio data stream along with its metadata. * @param startOfSpeechTimestamp Moment in time when user started talking to Alexa. This parameter is optional * and it is used to measure user perceived latency. + * @param beginIndex An optional parameter indicating where in the stream to start reading from. * @return A future indicating whether the interaction was successfully started. */ virtual std::future notifyOfHoldToTalkStart( capabilityAgents::aip::AudioProvider holdToTalkAudioProvider, - std::chrono::steady_clock::time_point startOfSpeechTimestamp = std::chrono::steady_clock::now()) = 0; + std::chrono::steady_clock::time_point startOfSpeechTimestamp = std::chrono::steady_clock::now(), + avsCommon::avs::AudioInputStream::Index beginIndex = + capabilityAgents::aip::AudioInputProcessor::INVALID_INDEX) = 0; /** * Ends a hold to talk interaction by forcing the client to stop streaming audio data to the cloud and ending any diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Timing/TimerDelegateFactoryInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Timing/TimerDelegateFactoryInterface.h new file mode 100644 index 0000000000..250f6f2a09 --- /dev/null +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Timing/TimerDelegateFactoryInterface.h @@ -0,0 +1,55 @@ +/* + * 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_TIMING_TIMERDELEGATEFACTORYINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_TIMING_TIMERDELEGATEFACTORYINTERFACE_H_ + +#include + +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace sdkInterfaces { +namespace timing { + +/// A factory for creating @c TimerDelegateInterface. +class TimerDelegateFactoryInterface { +public: + /// Destructor. + virtual ~TimerDelegateFactoryInterface() = default; + + /** + * Whether or not this @c TimerDelegateInterface can operate when the system is running in a reduced power mode. + * Examples of this include an implementation which uses a Real Time Clock. + * + * @return A boolean whether this is supported. + */ + virtual bool supportsLowPowerMode() = 0; + + /** + * Create an instance of a @c TimerDelegateInterface. This must be non-null. + * + * @return A ptr to a @c TimerDelegateInterface. + */ + virtual std::unique_ptr getTimerDelegate() = 0; +}; + +} // namespace timing +} // namespace sdkInterfaces +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_TIMING_TIMERDELEGATEFACTORYINTERFACE_H_ diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Timing/TimerDelegateInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Timing/TimerDelegateInterface.h new file mode 100644 index 0000000000..de9bf9c5f9 --- /dev/null +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/Timing/TimerDelegateInterface.h @@ -0,0 +1,121 @@ +/* + * 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_TIMING_TIMERDELEGATEINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_TIMING_TIMERDELEGATEINTERFACE_H_ + +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace sdkInterfaces { +namespace timing { + +/** + * A class which contains Timer logic that runs a task after a certain delay. + * Implementations of this MUST be thread safe. + */ +class TimerDelegateInterface { +public: + /// Destructor. + virtual ~TimerDelegateInterface() = default; + + /** + * Value for @c start()'s @c maxCount parameter which indicates that the @c TimerDelegateInterface + * should continue firing indefinitely. + */ + static const size_t FOREVER = 0; + + /// Static member function to get FOREVER. + static size_t getForever() { + return FOREVER; + } + + /// Specifies different ways to apply the period of a recurring task. + enum class PeriodType { + /** + * The period specifies the time from the start of one task call to the start of the next task call. This + * period type ensures task calls occur on a predictable cadence. + * + * @note A timer makes one task call at a time, so if a task call takes more than one period to execute, + * the subsequent calls which would have occured while the task was still executing will be skipped, and + * the next call will not occur until the next period-multiple after the original task call completes. + */ + ABSOLUTE, + + /** + * The period specifies the time from the end of one task call to the start of the next task call. This period + * type ensures a specific amount of idle time between task calls. + */ + RELATIVE + }; + + /** + * Waits for the @c delay, then calls @c task periodically. + * + * @param delay The non-negative time to wait before making the first @c task call. + * @param period The non-negative time to wait between subsequent @c task calls. + * @param periodType The type of period to use when making subsequent task calls. + * @param maxCount The desired number of times to call task. + * @c TimerDelegateInterface::getForever() means to call forever until + * @c stop() is called. Note that fewer than @c maxCount calls may occur if @c periodType is + * @c PeriodType::ABSOLUTE and the task runtime exceeds @c period. + * @param task A callable type representing a task. + */ + virtual void start( + std::chrono::nanoseconds delay, + std::chrono::nanoseconds period, + PeriodType periodType, + size_t maxCount, + std::function task) = 0; + + /** + * Stops the @c TimerDelegateInterface (if running). This should not interrupt an active call to the task, + * but will prevent any subequent calls to the task. If @c stop() is called while the task is executing, + * this function will block until the task completes. + * + * @note In the special case that @c stop() is called from inside the task function, @c stop() will still + * prevent any subsequent calls to the task, but will *not* block as described above. + */ + virtual void stop() = 0; + + /** + * Marks this @c TimerDelegateInterface as active for strict ordering purposes. When called, the implementation + * must atomically set the internal state as active. Functionally this method must behave as an + * std::atomic_exchange operation. + * + * @returns @c true if the @c Timer was previously inactive, else @c false. + */ + virtual bool activate() = 0; + + /** + * Reports whether the @c TimerDelegateInterface is active. A timer is considered active if it is waiting + * to start a call to the task, or if a call to the task is in progress. + * Examples of these can be after calls to activate() or start(). + * A timer is only considered inactive if it has not been started, + * if all requested/scheduled calls to the task have completed, or after a call to @c stop(). + * + * @returns @c true if the @c Timer is active, else @c false. + */ + virtual bool isActive() const = 0; +}; + +} // namespace timing +} // namespace sdkInterfaces +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_TIMING_TIMERDELEGATEINTERFACE_H_ diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ToggleController/ToggleControllerAttributeBuilderInterface.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ToggleController/ToggleControllerAttributeBuilderInterface.h index 925e39a054..4a81720ee6 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ToggleController/ToggleControllerAttributeBuilderInterface.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ToggleController/ToggleControllerAttributeBuilderInterface.h @@ -17,6 +17,7 @@ #define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_TOGGLECONTROLLER_TOGGLECONTROLLERATTRIBUTEBUILDERINTERFACE_H_ #include +#include #include #include "ToggleControllerAttributes.h" @@ -52,6 +53,16 @@ class ToggleControllerAttributeBuilderInterface { virtual ToggleControllerAttributeBuilderInterface& withCapabilityResources( const avsCommon::avs::CapabilityResources& capabilityResources) = 0; + /** + * Configures the builder to use the specified semantics definition. + * @see https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/alexa-togglecontroller.html + * + * @param semantics The @c CapabilitySemantics representing the semantics definition. + * @return This builder, which can be used to nest configuration function calls. + */ + virtual ToggleControllerAttributeBuilderInterface& withSemantics( + const avsCommon::avs::capabilitySemantics::CapabilitySemantics& semantics) = 0; + /** * Builds a @c ToggleControllerAttributes with the configured properties. * diff --git a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ToggleController/ToggleControllerAttributes.h b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ToggleController/ToggleControllerAttributes.h index abfd469215..7d01e219c6 100644 --- a/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ToggleController/ToggleControllerAttributes.h +++ b/AVSCommon/SDKInterfaces/include/AVSCommon/SDKInterfaces/ToggleController/ToggleControllerAttributes.h @@ -17,6 +17,8 @@ #define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_INCLUDE_AVSCOMMON_SDKINTERFACES_TOGGLECONTROLLER_TOGGLECONTROLLERATTRIBUTES_H_ #include +#include +#include namespace alexaClientSDK { namespace avsCommon { @@ -24,12 +26,55 @@ namespace sdkInterfaces { namespace toggleController { /** - * The toggle controller attribute containing the capability resources required for + * The Toggle Controller attributes containing the capability resources required for * Capability Agent discovery. * * @see https://developer.amazon.com/docs/alexa/alexa-voice-service/alexa-togglecontroller.html#capability-assertion */ -using ToggleControllerAttributes = avsCommon::avs::CapabilityResources; +struct ToggleControllerAttributes { + /** + * Default constructor. + * + * @note Avoid using this method. This was added only to enable the to use @c Optional::value(). + */ + ToggleControllerAttributes() = default; + + /** + * Constructor to build the @c ToggleControllerAttributes using provided values. + * + * @param capabilityResources The capability resources. + */ + ToggleControllerAttributes(const avsCommon::avs::CapabilityResources& capabilityResources); + + /** + * Constructor to build the @c ToggleControllerAttributes using provided values. + * + * @param capabilityResources The capability resources. + * @param semantics The optional semantics definition. + */ + ToggleControllerAttributes( + const avsCommon::avs::CapabilityResources& capabilityResources, + avsCommon::utils::Optional semantics); + + /// The capability resources as @c CapabilityResources + const avsCommon::avs::CapabilityResources capabilityResources; + + /// A semantics definition as an @c Optional @c CapabilitySemantics + avsCommon::utils::Optional semantics; +}; + +inline ToggleControllerAttributes::ToggleControllerAttributes( + const avsCommon::avs::CapabilityResources& capabilityResources) : + capabilityResources{capabilityResources}, + semantics{avsCommon::utils::Optional()} { +} + +inline ToggleControllerAttributes::ToggleControllerAttributes( + const avsCommon::avs::CapabilityResources& capabilityResources, + avsCommon::utils::Optional semantics) : + capabilityResources{capabilityResources}, + semantics{semantics} { +} } // namespace toggleController } // namespace sdkInterfaces diff --git a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Endpoints/MockEndpointCapabilitiesRegistrar.h b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Endpoints/MockEndpointCapabilitiesRegistrar.h new file mode 100644 index 0000000000..f8ce654e04 --- /dev/null +++ b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/Endpoints/MockEndpointCapabilitiesRegistrar.h @@ -0,0 +1,57 @@ +/* + * 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_TEST_AVSCOMMON_SDKINTERFACES_ENDPOINTS_MOCKENDPOINTCAPABILITIESREGISTRAR_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_TEST_AVSCOMMON_SDKINTERFACES_ENDPOINTS_MOCKENDPOINTCAPABILITIESREGISTRAR_H_ + +#include + +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace sdkInterfaces { +namespace endpoints { +namespace test { + +/// Mocks an endpoint capabilities registrar. +class MockEndpointCapabilitiesRegistrar : public EndpointCapabilitiesRegistrarInterface { +public: + /// @name @c MockEndpointCapabilitiesRegistrarInterface methods to be mocked. + /// @{ + MOCK_METHOD2( + withCapability, + EndpointCapabilitiesRegistrarInterface&( + const avs::CapabilityConfiguration& configuration, + std::shared_ptr directiveHandler)); + MOCK_METHOD2( + withCapability, + EndpointCapabilitiesRegistrarInterface&( + const std::shared_ptr& configurationInterface, + std::shared_ptr directiveHandler)); + MOCK_METHOD1( + withCapabilityConfiguration, + EndpointCapabilitiesRegistrarInterface&( + const std::shared_ptr& configurationInterface)); + /// @} +}; + +} // namespace test +} // namespace endpoints +} // namespace sdkInterfaces +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_TEST_AVSCOMMON_SDKINTERFACES_ENDPOINTS_MOCKENDPOINTCAPABILITIESREGISTRAR_H_ diff --git a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockAVSConnectionManager.h b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockAVSConnectionManager.h index c22471449b..d485776c38 100644 --- a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockAVSConnectionManager.h +++ b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockAVSConnectionManager.h @@ -34,7 +34,6 @@ class MockAVSConnectionManager : public AVSConnectionManagerInterface { MOCK_METHOD0(reconnect, void()); MOCK_CONST_METHOD0(isConnected, bool()); MOCK_METHOD0(onWakeConnectionRetry, void()); - MOCK_METHOD0(onWakeVerifyConnectivity, void()); MOCK_METHOD1( addMessageObserver, void(std::shared_ptr observer)); @@ -43,6 +42,9 @@ class MockAVSConnectionManager : public AVSConnectionManagerInterface { void(std::shared_ptr observer)); MOCK_METHOD1(addConnectionStatusObserver, void(std::shared_ptr observer)); MOCK_METHOD1(removeConnectionStatusObserver, void(std::shared_ptr observer)); + MOCK_METHOD1(sendMessage, void(std::shared_ptr request)); + MOCK_METHOD1(setAVSGateway, void(const std::string& avsGateway)); + MOCK_CONST_METHOD0(getAVSGateway, std::string()); }; } // namespace test } // namespace sdkInterfaces diff --git a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockAVSGatewayAssigner.h b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockAVSGatewayAssigner.h index 2c709d2185..0849890b80 100644 --- a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockAVSGatewayAssigner.h +++ b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockAVSGatewayAssigner.h @@ -30,6 +30,7 @@ namespace test { class MockAVSGatewayAssigner : public AVSGatewayAssignerInterface { public: MOCK_METHOD1(setAVSGateway, void(const std::string& avsGateway)); + MOCK_CONST_METHOD0(getAVSGateway, std::string()); }; } // namespace test diff --git a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockPowerResourceManager.h b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockPowerResourceManager.h index da0310aa0c..fdf3957f1a 100644 --- a/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockPowerResourceManager.h +++ b/AVSCommon/SDKInterfaces/test/AVSCommon/SDKInterfaces/MockPowerResourceManager.h @@ -16,9 +16,12 @@ #ifndef ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_TEST_AVSCOMMON_SDKINTERFACES_MOCKPOWERRESOURCEMANAGER_H_ #define ALEXA_CLIENT_SDK_AVSCOMMON_SDKINTERFACES_TEST_AVSCOMMON_SDKINTERFACES_MOCKPOWERRESOURCEMANAGER_H_ -#include "AVSCommon/SDKInterfaces/PowerResourceManagerInterface.h" +#include + #include +#include "AVSCommon/SDKInterfaces/PowerResourceManagerInterface.h" + namespace alexaClientSDK { namespace avsCommon { namespace sdkInterfaces { @@ -32,8 +35,34 @@ class MockPowerResourceManager : public PowerResourceManagerInterface { MOCK_METHOD2(acquirePowerResource, void(const std::string& component, const PowerResourceLevel level)); MOCK_METHOD1(releasePowerResource, void(const std::string& component)); MOCK_METHOD1(isPowerResourceAcquired, bool(const std::string& component)); + MOCK_METHOD3( + create, + std::shared_ptr( + const std::string& resourceId, + bool isRefCounted, + const PowerResourceLevel level)); + MOCK_METHOD2( + acquire, + bool(const std::shared_ptr& id, const std::chrono::milliseconds autoReleaseTimeout)); + MOCK_METHOD1(release, bool(const std::shared_ptr& id)); + MOCK_METHOD1(close, bool(const std::shared_ptr& id)); + + /** + * Some methods require default behavior to be useful, even in a mock. This function sets those default behaviors. + */ + void setDefaultBehavior(); }; +inline void MockPowerResourceManager::setDefaultBehavior() { + ON_CALL(*this, create(testing::_, testing::_, testing::_)) + .WillByDefault( + testing::Invoke([](const std::string& resourceId, + bool isRefCounted, + const sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel level) { + return std::make_shared(resourceId); + })); +} + } // namespace test } // namespace sdkInterfaces } // namespace avsCommon diff --git a/AVSCommon/SDKInterfaces/test/CMakeLists.txt b/AVSCommon/SDKInterfaces/test/CMakeLists.txt index bb04f67835..088e45442c 100644 --- a/AVSCommon/SDKInterfaces/test/CMakeLists.txt +++ b/AVSCommon/SDKInterfaces/test/CMakeLists.txt @@ -1,6 +1,5 @@ if (BUILD_TESTING) add_library(SDKInterfacesTests - src/EqualizerStorageInterfaceTest.cpp src/StubMiscStorage.cpp ) diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Endian.h b/AVSCommon/Utils/include/AVSCommon/Utils/Endian.h index 9c43b547d2..037d68c6d2 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Endian.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Endian.h @@ -29,6 +29,20 @@ inline bool littleEndianMachine() { return words[0] == 1; } +// Function used to swap endian of uint16_t +inline uint16_t swapEndian(uint16_t input) { + return ((input & 0x00ff) << 8 | (input & 0xff00) >> 8); +} + +// Function used to swap endian of uint32_t +inline uint32_t swapEndian(uint32_t input) { + return ( + (input & 0xff000000) >> 24 | // move byte 3 to byte 0 + (input & 0x0000ff00) << 8 | // move byte 1 to byte 2 + (input & 0x00ff0000) >> 8 | // move byte 2 to byte 1 + (input & 0x000000ff) << 24); // byte 0 to byte 3 +} + } // namespace utils } // namespace avsCommon } // namespace alexaClientSDK diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/HTTP/HttpResponseCode.h b/AVSCommon/Utils/include/AVSCommon/Utils/HTTP/HttpResponseCode.h index 9e46e1d044..b9306264e4 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/HTTP/HttpResponseCode.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/HTTP/HttpResponseCode.h @@ -58,11 +58,15 @@ enum HTTPResponseCode { CLIENT_ERROR_BAD_REQUEST = 400, /// HTTP code for forbidden request by user. CLIENT_ERROR_FORBIDDEN = 403, + /// HTTP code for too many requests to the service. + CLIENT_ERROR_THROTTLING_EXCEPTION = 429, /// HTTP code for internal error by server which didn't fulfill the request. SERVER_ERROR_INTERNAL = 500, /// HTTP code for internal error by server for not supporting the facility requested. SERVER_ERROR_NOT_IMPLEMENTED = 501, + /// HTTP code for service not available + SERVER_UNAVAILABLE = 503, /// First success code SUCCESS_START_CODE = SUCCESS_OK, @@ -136,8 +140,12 @@ inline HTTPResponseCode intToHTTPResponseCode(int code) { return HTTPResponseCode::CLIENT_ERROR_BAD_REQUEST; case 403: return HTTPResponseCode::CLIENT_ERROR_FORBIDDEN; + case 429: + return HTTPResponseCode::CLIENT_ERROR_THROTTLING_EXCEPTION; case 500: return HTTPResponseCode::SERVER_ERROR_INTERNAL; + case 503: + return HTTPResponseCode::SERVER_UNAVAILABLE; } logger::acsdkError( logger::LogEntry("HttpResponseCodes", __func__).d("code", code).m("Unknown HTTP response code.")); @@ -192,10 +200,14 @@ inline std::string responseCodeToString(HTTPResponseCode responseCode) { return "CLIENT_ERROR_BAD_REQUEST"; case HTTPResponseCode::CLIENT_ERROR_FORBIDDEN: return "CLIENT_ERROR_FORBIDDEN"; + case HTTPResponseCode::CLIENT_ERROR_THROTTLING_EXCEPTION: + return "CLIENT_ERROR_THROTTLING_EXCEPTION"; case HTTPResponseCode::SERVER_ERROR_INTERNAL: return "SERVER_ERROR_INTERNAL"; case HTTPResponseCode::SERVER_ERROR_NOT_IMPLEMENTED: return "SERVER_ERROR_NOT_IMPLEMENTED"; + case HTTPResponseCode::SERVER_UNAVAILABLE: + return "SERVER_UNAVAILABLE"; } logger::acsdkError(logger::LogEntry("HttpResponseCodes", __func__) .d("longValue", static_cast(responseCode)) diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/CurlEasyHandleWrapper.h b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/CurlEasyHandleWrapper.h index 8458bd82c0..73764aba38 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/CurlEasyHandleWrapper.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/CurlEasyHandleWrapper.h @@ -238,6 +238,15 @@ class CurlEasyHandleWrapper { */ long getHTTPResponseCode(); + /** + * Get the effective URL. + * @note In cases where CURLOPT_FOLLOWLOCATION is used, CURL follows the url it receives in the location header + * of the response until it reaches a URL with no location header. This last used url is the effective URL. + * + * @return The effective URL string. + */ + std::string getEffectiveUrl(); + /** * Perform whatever has been setup in the handle. * diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/HTTPContentFetcherFactory.h b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/HTTPContentFetcherFactory.h index 53fd3f8d43..93e5a0f487 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/HTTPContentFetcherFactory.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/HTTPContentFetcherFactory.h @@ -32,7 +32,25 @@ namespace libcurlUtils { */ class HTTPContentFetcherFactory : public avsCommon::sdkInterfaces::HTTPContentFetcherInterfaceFactoryInterface { public: + /** + * Factory for creating instances of avsCommon::sdkInterfaces::HTTPContentFetcherInterfaceFactoryInterface + * + * @return A new instance of avsCommon::sdkInterfaces::HTTPContentFetcherInterfaceFactoryInterface. + */ + static std::shared_ptr + createHTTPContentFetcherInterfaceFactoryInterface(); + + /** + * Constructor. + * + * @deprecated + */ + HTTPContentFetcherFactory() = default; + + /// @name HTTPContentFetcherInterfaceFactoryInterface methods + /// @{ std::unique_ptr create(const std::string& url) override; + /// @} }; } // namespace libcurlUtils diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibCurlHttpContentFetcher.h b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibCurlHttpContentFetcher.h index f4741ac178..994de9a186 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibCurlHttpContentFetcher.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibCurlHttpContentFetcher.h @@ -41,6 +41,7 @@ class LibCurlHttpContentFetcher : public avsCommon::sdkInterfaces::HTTPContentFe /// @{ State getState() override; std::string getUrl() const override; + std::string getEffectiveUrl() const override; Header getHeader(std::atomic* shouldShutdown) override; bool getBody(std::shared_ptr writer) override; void shutdown() override; @@ -83,6 +84,12 @@ class LibCurlHttpContentFetcher : public avsCommon::sdkInterfaces::HTTPContentFe /// The URL to fetch from. const std::string m_url; + /// Mutex to synchronize access to m_effectiveUrl. + mutable std::mutex m_effectiveUrlMutex; + + /// The last used url to fetch content from. + std::string m_effectiveUrl; + /// A libcurl wrapper. CurlEasyHandleWrapper m_curlWrapper; @@ -153,6 +160,12 @@ class LibCurlHttpContentFetcher : public avsCommon::sdkInterfaces::HTTPContentFe * @return @c true if it is still waiting for the method call. */ bool waitingForBodyRequest(); + + /** + * Updates the effective URL using CURL's inbuilt functions. + * @note: This method should only be called after the curl multi perform method. + */ + void updateEffectiveURL(); }; } // namespace libcurlUtils diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2ConnectionFactory.h b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2ConnectionFactory.h index d7855f73ff..8fa29ea390 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2ConnectionFactory.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/LibcurlUtils/LibcurlHTTP2ConnectionFactory.h @@ -28,6 +28,14 @@ namespace libcurlUtils { */ class LibcurlHTTP2ConnectionFactory : public avsCommon::utils::http2::HTTP2ConnectionFactoryInterface { public: + /** + * Create a new instance of @c LibcurlHTTP2ConnectionFactory. + * + * @return A new instance of @c LibcurlHTTP2ConnectionFactory. + */ + static std::shared_ptr + createHTTP2ConnectionFactoryInterface(); + /// @name HTTP2ConnectionFactoryInterface methods. /// @{ std::shared_ptr createHTTP2Connection() override; diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerFactoryInterface.h b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerFactoryInterface.h index 4abfa8c055..bde0044983 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerFactoryInterface.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerFactoryInterface.h @@ -47,8 +47,10 @@ struct Fingerprint { }; /** - * A @c MediaPlayerFactoryInterface allows access to @c MediaPlayerInterface instances as needed (and if availible) - * This is a capability needed to support pre-buffering + * A @c MediaPlayerFactoryInterface allows access to @c MediaPlayerInterface instances as needed (and if available). + * This is a capability needed to support pre-buffering. + * + * @deprecated Use PooledMediaResourceProviderInterface instead. * * Instances are not expected to be Thread-safe. */ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerObserverInterface.h b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerObserverInterface.h index 8e946b682b..e33bf575e3 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerObserverInterface.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerObserverInterface.h @@ -232,7 +232,11 @@ class MediaPlayerObserverInterface { * @return The @c ostream that was passed in and written to. */ inline std::ostream& operator<<(std::ostream& stream, const MediaPlayerState& state) { - return stream << "MediaPlayerState: offsetInMilliseconds=" << state.offset.count(); + stream << "MediaPlayerState: offsetInMilliseconds=" << state.offset.count(); + if (state.mediaPlayerProtection.hasValue()) { + stream << "," << state.mediaPlayerProtection.value(); + } + return stream; } } // namespace mediaPlayer diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerState.h b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerState.h index 96af7e03b7..32b9bdd938 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerState.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/MediaPlayerState.h @@ -16,6 +16,8 @@ #ifndef ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_MEDIAPLAYER_MEDIAPLAYERSTATE_H_ #define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_MEDIAPLAYER_MEDIAPLAYERSTATE_H_ +#include + #include namespace alexaClientSDK { @@ -29,6 +31,48 @@ const std::chrono::milliseconds DURATION_UNKNOWN = std::chrono::milliseconds(-1) * Structure to hold metadata about the MediaPlayerState */ struct MediaPlayerState { + /** + * Structure to hold media protection information. + */ + struct MediaPlayerProtection { + /// Default Constructor + MediaPlayerProtection() : clearLead(false) { + } + + /// Constructor + MediaPlayerProtection(const std::string& protectionScheme, bool clearLead) : + protectionScheme(protectionScheme), + clearLead(clearLead) { + } + + /** + * Overload the == operator for equality checks + * + * @param other The other @c MediaPlayerProtection to compare + * @return Whether @c this is equivalent to @c other + */ + bool operator==(const MediaPlayerProtection& other) const { + return (protectionScheme == other.protectionScheme && clearLead == other.clearLead); + } + + /// Is the track protected + bool isProtected() const { + return protectionScheme.size() != 0; + } + + /** + * Name of the protection scheme used to protect media. + * This field will be empty if no protection scheme is used. + */ + std::string protectionScheme; + + /** + * Whether we found some clear lead data, possibly to mitigate latency, even though most of the + * content was protected. + */ + bool clearLead; + }; + /** * Default Constructor, initializes the offset to zero. */ @@ -43,12 +87,27 @@ struct MediaPlayerState { duration(duration_) { } + /** + * Constructor. + */ + MediaPlayerState( + std::chrono::milliseconds offsetInMs, + const MediaPlayerProtection& aMediaPlayerProtection, + std::chrono::milliseconds duration_ = DURATION_UNKNOWN) : + offset(offsetInMs), + duration(duration_), + mediaPlayerProtection(aMediaPlayerProtection) { + } + /// Offset in milliseconds std::chrono::milliseconds offset; /// Duration std::chrono::milliseconds duration; + /// Optional: MediaPlayerProtection information. + Optional mediaPlayerProtection; + /** * Overload the == operator for equality checks * @@ -56,10 +115,29 @@ struct MediaPlayerState { * @return Whether @c this is equivalent to @c other */ bool operator==(const MediaPlayerState& other) const { - return offset == other.offset; + return offset == other.offset && duration == other.duration && + mediaPlayerProtection == other.mediaPlayerProtection; } }; +/** + * Write a @c MediaPlayerProtection value to an @c ostream. + * + * @param stream The stream to write the value to. + * @param mediaPlayerProtection The @c MediaPlayerProtection 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 MediaPlayerState::MediaPlayerProtection& mediaPlayerProtection) { + stream << "MediaPlayerProtection: isProtected=" << std::boolalpha << mediaPlayerProtection.isProtected(); + if (mediaPlayerProtection.isProtected()) { + stream << ",protectionScheme=" << mediaPlayerProtection.protectionScheme << ",clearLead=" << std::boolalpha + << mediaPlayerProtection.clearLead; + } + return stream; +} + } // namespace mediaPlayer } // namespace utils } // namespace avsCommon diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PlaybackContext.h b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PlaybackContext.h index 25bb5f751d..c20f2cd047 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PlaybackContext.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PlaybackContext.h @@ -61,6 +61,14 @@ struct PlaybackContext { HeaderConfig allConfig; }; +/** + * Check for malicious characters(\r, \n, null) in the header string. + * + * @param header to be validated. + * @return @c true if not malicious, else @c false. + */ +bool validateIfNotMalicious(const std::string& header); + /** * Validate the headers. Delete the invalid entries * diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PooledMediaPlayerFactory.h b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PooledMediaPlayerFactory.h index 7217c1dc70..ef663b225d 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PooledMediaPlayerFactory.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PooledMediaPlayerFactory.h @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include @@ -33,9 +33,10 @@ namespace mediaPlayer { class PooledMediaPlayerFactory : public avsCommon::utils::mediaPlayer::MediaPlayerFactoryInterface { public: /** - * Create a PooledMediaPlayerFactory from a pre-created set of Players. + * Create a @c PooledMediaPlayerFactory from a pre-created set of Players. * - * @param pool pre-created collection of MediaPlayers. Ownership is not transferred. + * @deprecated Use adaptMediaPlayerFactoryInterface. + * @param pool pre-created collection of MediaPlayers. Ownership is not transferred. */ static std::unique_ptr create( const std::vector>& pool, @@ -43,7 +44,7 @@ class PooledMediaPlayerFactory : public avsCommon::utils::mediaPlayer::MediaPlay virtual ~PooledMediaPlayerFactory(); - /// @name MediaPlayerFactoryInterface methods. + /// @name PooledMediaResourceProviderInterface methods. ///@{ avsCommon::utils::mediaPlayer::Fingerprint getFingerprint() override; std::shared_ptr acquireMediaPlayer() override; @@ -57,13 +58,16 @@ class PooledMediaPlayerFactory : public avsCommon::utils::mediaPlayer::MediaPlay protected: /** - * Constructor used to create a PooledMediaPlayerFactory from a pre-created set of Players. + * Constructor used to create a PooledMediaPlayerFactory from a pre-created set of players and their associated + * speakers, channel volumes, and equalizers. * - * @param pool pre-created collection of MediaPlayers. Ownership is not transferred. + * @param mediaPlayerPool Pre-created collection of MediaPlayers. + * @param fingerprint Optional fingerprint argument to send to AVS. */ PooledMediaPlayerFactory( - const std::vector>& pool, - const avsCommon::utils::mediaPlayer::Fingerprint& fingerprint); + const std::vector>& mediaPlayerPool, + const avsCommon::utils::mediaPlayer::Fingerprint& fingerprint = {}); + /** * Synchronously notify all observers that a player is available */ @@ -73,6 +77,7 @@ class PooledMediaPlayerFactory : public avsCommon::utils::mediaPlayer::MediaPlay std::vector> m_availablePlayerPool; /// The collection of players in use std::vector> m_inUsePlayerPool; + /// The collection of @c ChannelVolumeInterfaces associated with the media players managed by this factory.. /// Factory observers std::unordered_set> m_observers; /// MediaPlayer version information diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PooledMediaResourceProvider.h b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PooledMediaResourceProvider.h new file mode 100644 index 0000000000..866b2bc7f0 --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PooledMediaResourceProvider.h @@ -0,0 +1,108 @@ +/* + * 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_MEDIAPLAYER_POOLEDMEDIARESOURCEPROVIDER_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_MEDIAPLAYER_POOLEDMEDIARESOURCEPROVIDER_H_ + +#include + +#include +#include +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace mediaPlayer { + +/** + * Adapts the legacy @c MediaPlayerFactoryInterface (which provides only media players) to the @c + * PooledMediaResourceProviderInterface (which can also provide other associated media resources with the media + * players). + * + */ +class PooledMediaResourceProvider : public avsCommon::utils::mediaPlayer::PooledMediaResourceProviderInterface { +public: + /** + * Adapts a legacy @c MediaPlayerFactoryInterface to a @c PooledMediaResourceProviderInterface, given the + * speakers associated with the players managed by the @c MediaPlayerFactoryInterface. + * + * @param mediaPlayerFactory The factory to adapt. + * @param speakers The @c ChannelVolumeInterfaces that the @c PooledMediaResourceProvider will return on + * getSpeakers(). + * @return A new @c PooledMediaResourceProviderInterface. + */ + static std::shared_ptr + adaptMediaPlayerFactoryInterface( + std::unique_ptr mediaPlayerFactory, + std::vector> speakers); + + /** + * Factory method that creates a @c PooledMediaResourceProviderInterface. + * + * @param mediaPlayers The pooled media players to manage. + * @param speakers The @c ChannelVolumeInterfaces that the @c PooledMediaResourceProvider will return on + * getSpeakers(). + * @param fingerprint Optional fingerprint argument to send to AVS. + * @return A new @c PooledMediaResourceProviderInterface. + */ + static std::shared_ptr + createPooledMediaResourceProviderInterface( + std::vector> mediaPlayers, + std::vector> speakers, + const avsCommon::utils::mediaPlayer::Fingerprint& fingerprint = {}); + + ~PooledMediaResourceProvider() override; + + /// @name PooledMediaResourceProviderInterface methods. + ///@{ + avsCommon::utils::mediaPlayer::Fingerprint getFingerprint() override; + std::shared_ptr acquireMediaPlayer() override; + bool releaseMediaPlayer(std::shared_ptr mediaPlayer) override; + bool isMediaPlayerAvailable() override; + void addObserver( + std::shared_ptr observer) override; + void removeObserver( + std::shared_ptr observer) override; + std::vector> getSpeakers() const override; + ///@} + +private: + /** + * Constructor used to create a PooledMediaResourceProvider from a @c MediaPlayerFactoryInterface and a vector + * of speakers. + * + * @param mediaPlayerFactory The factory to adapt. + * @param speakers The @c ChannelVolumeInterfaces that the @c PooledMediaResourceProvider will return on + * getSpeakers(). + */ + PooledMediaResourceProvider( + std::unique_ptr mediaPlayerFactory, + std::vector> speakers); + + /// The factory being adapted by this instance. + std::unique_ptr m_factory; + + /// The collection of associated @c ChannelVolumeInterfaces. + std::vector> m_speakers; +}; + +} // namespace mediaPlayer +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_MEDIAPLAYER_POOLEDMEDIARESOURCEPROVIDER_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PooledMediaResourceProviderInterface.h b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PooledMediaResourceProviderInterface.h new file mode 100644 index 0000000000..d034b21e3e --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/MediaPlayer/PooledMediaResourceProviderInterface.h @@ -0,0 +1,62 @@ +/* + * 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_MEDIAPLAYER_POOLEDMEDIARESOURCEPROVIDERINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_MEDIAPLAYER_POOLEDMEDIARESOURCEPROVIDERINTERFACE_H_ + +#include +#include + +#include + +#include "AVSCommon/Utils/MediaPlayer/MediaPlayerFactoryInterface.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace mediaPlayer { + +/** + * A @c PooledMediaResourceProviderInterface allows access to @c MediaPlayerInterface instances as needed (and if + * available). This is a capability needed to support pre-buffering. + * + * It also provides access to the @c ChannelVolumeInterfaces associated with the media players managed by this provider + * in order to perform volume adjustments on the players. + * + * Instances are not expected to be Thread-safe. + */ +class PooledMediaResourceProviderInterface : public MediaPlayerFactoryInterface { +public: + /** + * Destructor. + */ + virtual ~PooledMediaResourceProviderInterface() = default; + + /** + * Gets all @c ChannelVolumeInterfaces associated with the media players managed by this instance. + * @note The number of channel volume interfaces is not guaranteed to match the number of media players managed + * by the instance. + * + * @return A vector of pointers to @c ChannelVolumeInterfaces. + */ + virtual std::vector> getSpeakers() const = 0; +}; + +} // namespace mediaPlayer +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_MEDIAPLAYER_POOLEDMEDIARESOURCEPROVIDERINTERFACE_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Metrics/UplCalculatorInterface.h b/AVSCommon/Utils/include/AVSCommon/Utils/Metrics/UplCalculatorInterface.h new file mode 100644 index 0000000000..04e5524018 --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Metrics/UplCalculatorInterface.h @@ -0,0 +1,65 @@ +/* + * 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_METRICS_UPLCALCULATORINTERFACE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_METRICS_UPLCALCULATORINTERFACE_H_ + +#include + +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace metrics { + +/** + * Interface class to inspect metrics and perform UPL analysis. + */ +class UplCalculatorInterface { +public: + /// Alias for convenience + using UplTimePoint = std::chrono::steady_clock::time_point; + + /** + * Destructor. + */ + virtual ~UplCalculatorInterface() = default; + + /** + * Inspect the given metric. If needed, record the metric and perform calculations. + * + * @param metricEvent is the @c MetricEvent to inspect. + */ + virtual void inspectMetric(const std::shared_ptr& metricEvent) = 0; + + /** + * Sets the uplData to the given pointer. + * + * @param uplData Object to record in. + */ + virtual void setUplData(const std::shared_ptr& uplData) = 0; + +protected: + std::shared_ptr m_uplData; +}; + +} // namespace metrics +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_METRICS_UPLCALCULATORINTERFACE_H_ \ No newline at end of file diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Metrics/UplData.h b/AVSCommon/Utils/include/AVSCommon/Utils/Metrics/UplData.h new file mode 100644 index 0000000000..80eb6d6e50 --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Metrics/UplData.h @@ -0,0 +1,125 @@ +/* + * 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_METRICS_UPLDATA_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_METRICS_UPLDATA_H_ + +#include +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace metrics { + +/** + * This class represents the UplData shared between UPL Calculators + */ +class UplData { +public: + /// Alias for convenience + using UplTimePoint = std::chrono::steady_clock::time_point; + + /** + * Add a metric TimePoint to the UplTimePointsMap. + * If an entry with the same name is already present, it will overwrite the previous UplTimePoint. + * + * @param name Metric name + * @param timepoint Time point of the metric + */ + void addTimepoint(const std::string& name, const UplTimePoint& timepoint); + + /** + * Returns a saved metric's TimePoint from the UplTimePointsMap. + * + * @param name Name of metric + * @returns Time point of the metric + */ + UplTimePoint getTimepoint(const std::string& name) const; + + /** + * Adds the time point of a specific directive's PARSE_COMPLETE metric to the MessageIdToParseCompleteMap. + * If an entry with the same directiveId is already present, it will overwrite the previous UplTimePoint. + * + * @param directiveId Id of directive parsed + * @param timepoint Time point of the PARSE_COMPLETE metric + */ + void addParseCompleteTimepoint(const std::string& directiveId, const UplTimePoint& timepoint); + + /** + * Returns the time point of a specific directive's PARSE_COMPLETE metric from the MessageIdToParseCompleteMap. + * + * @param directiveId Id of directive parsed + * @returns Time point of the PARSE_COMPLETE metric + */ + UplTimePoint getParseCompleteTimepoint(const std::string& directiveId) const; + + /** + * Adds the time point of a specific directive's DIRECTIVE_DISPATCHED metric to the + * MessageIdToDirectiveDispatchedMap. + * If an entry with the same directiveId is already present, it will overwrite the previous UplTimePoint. + * + * @param directiveId Id of directive dispatched + * @param timepoint Time point of the DIRECTIVE_DISPATCHED metric + */ + void addDirectiveDispatchedTimepoint(const std::string& directiveId, const UplTimePoint& timepoint); + + /** + * Returns the time point of a specific directive's DIRECTIVE_DISPATCHED metric from the + * MessageIdToDirectiveDispatchedMap. + * + * @param directiveId Id of directive dispatched + * @returns Time point of the DIRECTIVE_DISPATCHED metric + */ + UplTimePoint getDirectiveDispatchedTimepoint(const std::string& directiveId) const; + + /** + * Adds a string data to OtherData. + * If an entry with the same name is already present, it will overwrite the previous string data. + * + * @param name Name of the data + * @param data Data string + */ + void addStringData(const std::string& name, const std::string& data); + + /** + * Returns a saved string data from OtherData. + * + * @param name Name of the data + * @returns Data string + */ + std::string getStringData(const std::string& name) const; + +private: + /// Map of metric names to their recorded time point + std::unordered_map m_uplTimePointsMap; + + /// Map of all PARSE_COMPLETE metrics' time point recorded by directive message ID + std::unordered_map m_messageIdToParseCompleteMap; + + /// Map of all DIRECTIVE_DISPATCHED metrics' time point recorded by directive message ID + std::unordered_map m_messageIdToDirectiveDispatchedMap; + + /// Map of all other relevant string data + std::unordered_map m_otherData; +}; + +} // namespace metrics +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_METRICS_UPLDATA_H_ \ No newline at end of file diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Network/InternetConnectionMonitor.h b/AVSCommon/Utils/include/AVSCommon/Utils/Network/InternetConnectionMonitor.h index 5f05346634..65b0bd2425 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Network/InternetConnectionMonitor.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Network/InternetConnectionMonitor.h @@ -38,9 +38,19 @@ namespace network { */ class InternetConnectionMonitor : public sdkInterfaces::InternetConnectionMonitorInterface { public: + /** + * Factory for creating instances of InternetConnectionMonitorInterface. + * + * @param contentFetcherFactory Factory for creating content fetchers with which to verify connectivity. + * @return An instance of InternetConnectionMonitor. + */ + static std::shared_ptr createInternetConnectionMonitorInterface( + const std::shared_ptr& contentFetcherFactory); + /** * Creates a InternetConnectionMonitor. * + * @deprecatd * @param contentFetcherFactory The content fetcher that will make the test run to an S3 endpoint. * @return A unique_ptr to the InternetConnectionMonitor instance. */ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/PlaylistParser/PlaylistEntry.h b/AVSCommon/Utils/include/AVSCommon/Utils/PlaylistParser/PlaylistEntry.h index 1e5898ebc0..03566dccf4 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/PlaylistParser/PlaylistEntry.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/PlaylistParser/PlaylistEntry.h @@ -100,6 +100,9 @@ struct EncryptionInfo { /// The initilization vector used for encryption. std::string initVector; + + /// The total duration of the content from the playlist + std::chrono::milliseconds totalDuration; }; /** diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Power/AggregatedPowerResourceManager.h b/AVSCommon/Utils/include/AVSCommon/Utils/Power/AggregatedPowerResourceManager.h new file mode 100644 index 0000000000..f73e7df9ec --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Power/AggregatedPowerResourceManager.h @@ -0,0 +1,148 @@ +/* + * 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_POWER_AGGREGATEDPOWERRESOURCEMANAGER_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_POWER_AGGREGATEDPOWERRESOURCEMANAGER_H_ + +#include +#include +#include +#include + +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace power { + +/** + * An AVS SDK implementation of @c PowerResourceManagerInterface which aggregates calls to the application provided + * @c PowerResourceManagerInterface. This implementation creates a @c PowerResourceManagerInterface::PowerResourceId + * for each @c PowerResourceLevel, and maps acquire/release/etc calls down to each level-aggregated @c PowerResourceId. + * + * This reduces the number of resources that are created from the perspective of the application provided @c + * PowerResourceManagerInterface. Additionally, it allows optimizations (such as deduping calls to reference counted + * resources) to be more effective. + * + * To reduce latency associated with create/close, aggregated PowerResourceIds are not closed dynamically, and will + * persist for the lifetime of the @c AggregatedPowerResourceManager. + * + * This class does not aggregate the acquirePowerResource/releasePowerResource related APIs. + */ +class AggregatedPowerResourceManager : public avsCommon::sdkInterfaces::PowerResourceManagerInterface { +public: + /** + * Create an instance of this class. + * + * @param powerResourceManager The application provided @c PowerResourceManagerInterface. + * @return An instance if successful, else nullptr. + */ + static std::shared_ptr create( + std::shared_ptr powerResourceManager); + + /// Destructor. + virtual ~AggregatedPowerResourceManager(); + + /// @name PowerResourceManagerInterface Legacy Methods + /// @{ + void acquirePowerResource( + const std::string& component, + const PowerResourceLevel level = PowerResourceLevel::STANDBY_MED) override; + void releasePowerResource(const std::string& component) override; + bool isPowerResourceAcquired(const std::string& component) override; + std::shared_ptr create( + const std::string& resourceId, + bool isRefCounted = true, + const PowerResourceLevel level = PowerResourceLevel::STANDBY_MED) override; + bool acquire( + const std::shared_ptr& id, + const std::chrono::milliseconds autoReleaseTimeout = std::chrono::milliseconds::zero()) override; + bool release( + const std::shared_ptr& id) override; + bool close( + const std::shared_ptr& id) override; + /// @} + +private: + /// This is used to key aggregated PowerResourceId to the level it is aggregated by. + using AggregatedPowerResourceMap = std::unordered_map< + avsCommon::sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel, + std::shared_ptr, + avsCommon::utils::functional::EnumClassHash>; + + /** + * A struct to track reference counting preference and level for a @c + * PowerResourceManagerInterface::PowerResourceId. + */ + struct PowerResourceInfo { + /** + * Constructor. + * + * @param isRefCounted Whether this resource is reference counted. + * @param level The power level. + */ + PowerResourceInfo( + bool isRefCounted, + avsCommon::sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel level); + + /// Whether this resource is reference counted. + const bool isRefCounted; + + /// The power level. + const avsCommon::sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel level; + + /// The current refCount. + uint64_t refCount; + }; + + /** + * Constructor + * + * @param powerResourceManager The application provided @c PowerResourceManagerInterface. + */ + AggregatedPowerResourceManager( + std::shared_ptr powerResourceManager); + + /** + * Generates a @c PowerResourceId for the given power level. If it already exists, this function will return + * the previously created @c PowerResourceId for the given power level. The lock must be held. + * + * @param level The level to create an aggregated object for. + */ + std::shared_ptr getAggregatedPowerResourceIdLocked( + const PowerResourceManagerInterface::PowerResourceLevel level); + + /// A mutex for synchronization. + std::mutex m_mutex; + + /// The underlying application provided @c PowerResourceManagerInterface. + std::shared_ptr m_appPowerResourceManager; + + /// A map of string resource identifier to PowerResourceInfo. The string is stored from the unique id passed into + /// the PowerResourceManagerInterface::create call. + std::unordered_map m_ids; + + /// The map of @c PowerResourceId objects that are grouped by level. + AggregatedPowerResourceMap m_aggregatedPowerResources; +}; + +} // namespace power +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_POWER_AGGREGATEDPOWERRESOURCEMANAGER_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Power/NoOpPowerResourceManager.h b/AVSCommon/Utils/include/AVSCommon/Utils/Power/NoOpPowerResourceManager.h new file mode 100644 index 0000000000..fe366ee6fc --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Power/NoOpPowerResourceManager.h @@ -0,0 +1,65 @@ +/* + * 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_POWER_NOOPPOWERRESOURCEMANAGER_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_POWER_NOOPPOWERRESOURCEMANAGER_H_ + +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace power { + +/** + * A no-op implemention of the @c PowerResourceManager to be used for fallback behavior. + */ +class NoOpPowerResourceManager : public avsCommon::sdkInterfaces::PowerResourceManagerInterface { +public: + /// @name PowerResourceManagerInterface Functions + /// @{ + void acquirePowerResource( + const std::string& component, + const PowerResourceLevel level = PowerResourceLevel::ACTIVE_HIGH) override{}; + void releasePowerResource(const std::string& component) override{}; + bool isPowerResourceAcquired(const std::string& component) override { + return false; + }; + std::shared_ptr create( + const std::string& resourceId, + bool isRefCounted = true, + const PowerResourceLevel level = PowerResourceLevel::STANDBY_MED) override { + return nullptr; + }; + bool acquire( + const std::shared_ptr& id, + const std::chrono::milliseconds autoReleaseTimeout = std::chrono::milliseconds::zero()) override { + return true; + }; + bool release(const std::shared_ptr& id) override { + return true; + }; + bool close(const std::shared_ptr& id) override { + return true; + }; + /// @} +}; + +} // namespace power +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_POWER_NOOPPOWERRESOURCEMANAGER_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Power/PowerMonitor.h b/AVSCommon/Utils/include/AVSCommon/Utils/Power/PowerMonitor.h new file mode 100644 index 0000000000..05db1b75f5 --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Power/PowerMonitor.h @@ -0,0 +1,154 @@ +/* + * 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_POWER_POWERMONITOR_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_POWER_POWERMONITOR_H_ + +#include +#include +#include +#include +#include + +#include +#include "AVSCommon/Utils/Power/PowerResource.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace power { + +/** + * A class providing power monitoring capabilities for the SDK. + * To use this object, activate() must first be called. + * + * This must be activated before any components are created that may have power management logic. + * For applications that wish to use any of the Power related primitives, this may mean calling activate() + * outside the SDK. + */ +class PowerMonitor { +public: + /** + * Returns an instance of @c PowerMonitor. This is guaranteed to be non-null. + * + * @return The singleton instance of @c PowerMonitor. + */ + static std::shared_ptr getInstance(); + + /** + * Iniitalizes the @c PowerMonitor with a @c PowerResourceManagerInterface and activates. + * + * @param powerManager A @c PowerResourceManagerInterface instance. + */ + void activate(std::shared_ptr powerManager); + + /** + * Whether the @c PowerMonitor has been activated with an @c PowerResourceManagerInterface. + * + * @return A bool indicating activation state. + */ + bool isActive(); + + /** + * Deactivate the @c PowerMonitor. This clears the internal map and resets the + * @c PowerManagerResourceMangerInterface instance. + * + * @return a PowerManagerResourceMangerInterface instance. + */ + void deactivate(); + + /** + * Get the @c PowerResourceManagerInterface. + * + * @return The @c PowerResourceManagerInterface. + */ + std::shared_ptr getPowerResourceManager(); + + /** + * Gets the @c PowerResource associated with the current thread, or create one if none are associated. + * The same identifier must be used with a particular thread. The creator of the thread PowerResource is + * responsible for cleaning it up via removeThreadPowerResource(). This should typically be done when + * the thread exits. + * + * @param identifier The identifier to create the thread @c PowerResource if none exists. + * @param level The @c PowerResourceLevel to use on first creation. Otherwise this is ignored. + * @return @c PowerResource associated with the current thread. + */ + std::shared_ptr getThreadPowerResourceOrCreate( + const std::string& identifier, + sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel level = + sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel::STANDBY_MED); + + /** + * Get the @c PowerResource associated with the current thread. + * + * @return @c PowerResource associated with the current thread. + */ + std::shared_ptr getThreadPowerResource(); + + /** + * Assigns a previously created @c PowerResource to a thread. This will not affect the state of the @c + * PowerResource. If there is a previously assigned @c PowerResource this will fail. + * + * @param powerResource The @c PowerResource to assign. + * @return The assigned @c PowerResource if successful, else a nullptr. + */ + std::shared_ptr assignThreadPowerResource(std::shared_ptr powerResource); + + /** + * Convenience method for creating a local (non-thread associated) @c PowerResource using + * getPowerManager(). + * + * @param The identifier to create the local @c PowerResource with. + * @param level The @c PowerResourceLevel to use. + * @return @c PowerResource. + */ + std::shared_ptr createLocalPowerResource( + const std::string& identifier, + sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel level = + sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel::STANDBY_MED); + + /** + * Remove the current @c PowerResource associated with this thread. + */ + void removeThreadPowerResource(); + +private: + /** + * Returns m_active. m_mutex must be locked before calling this. + * + * @return A bool indicating activation state. + */ + bool isActiveLocked(); + + /// Mutex. + static std::mutex m_mutex; + + /// Singleton instance to the @c PowerMonitor. + static std::shared_ptr m_monitor; + + /// The underlying @c PowerManagerResourceInterface. + std::shared_ptr m_powerManager; + + /// A map of thread to @c PowerResource. + std::unordered_map> m_threadPowerResources; +}; + +} // namespace power +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_POWER_POWERMONITOR_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Power/PowerResource.h b/AVSCommon/Utils/include/AVSCommon/Utils/Power/PowerResource.h new file mode 100644 index 0000000000..b00620775a --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Power/PowerResource.h @@ -0,0 +1,165 @@ +/* + * 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_POWER_POWERRESOURCE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_POWER_POWERRESOURCE_H_ + +#include +#include +#include +#include + +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace power { + +/** + * An object representing a configuration of power level preferences. + * + * Behavior is undefined if direct calls are made to @c PowerResourceManagerInterface using + * the same component identifier as one associated with a @c PowerResource object. + */ +class PowerResource { +public: + /** + * Destructor. This will release all acquired instances. + */ + ~PowerResource(); + + /** + * Prefix that will be internally appended before calling @c PowerResourceManagerInterface. + */ + static constexpr const char* PREFIX = "ACSDK_"; + + /** + * Creates an instance of the @c PowerResource. + * + * @param identifier The identifier. This identifier must be unique across all instances, as it will be used + * to call the underlying @c PowerResourceManagerInterface. This will be prefixed internally with + * ACSDK_ to maintain uniqueness within @c PowerResourceManagerInterface. + * @param powerManager A pointer to the underlying @c PowerResourceManagerInterface. + * @param level The level to create this resource with. + * @param refCounted Whether refcounting is enabled. + * + * @return An instance. + */ + static std::shared_ptr create( + const std::string& identifier, + std::shared_ptr powerManager, + sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel level = + sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel::STANDBY_MED, + bool refCounted = true); + + /** + * Returns the id. This will equal the identifier passed into the constructor without the internal prefix. + * + * @return A string representing the id. + */ + std::string getId() const; + + /** + * Returns whether the current resource is refCounted. + * + * @return A bool indicating refCount state. + */ + bool isRefCounted() const; + + /** + * Returns whether the current resource is frozen. + * + * @return A bool indicating frozen state. + */ + bool isFrozen() const; + + /** + * Get the current level. + * + * @return The current level. + */ + sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel getLevel() const; + + /** + * Acquire a count of the resource. + */ + void acquire(); + + /** + * Release a count of the resource. + */ + void release(); + + /** + * Freezes the resource, and caches the current refcount. Any calls to acquire or release will no-op while + * the @c PowerResource is frozen. + */ + void freeze(); + + /** + * Thaws the resource, and re-acquires the amount of times the resource has been acquired. + */ + void thaw(); + +private: + /** + * Constructor + * + * @param identifier The identifier. + * @param powerManager A pointer to the underlying @c PowerResourceManagerInterface. + * @param level The level to create this resource with. + * @param refCounted Whether refcounting is enabled. + */ + PowerResource( + const std::string& identifier, + std::shared_ptr powerManager, + sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel level, + bool refCounted); + + /// Identifier name. + const std::string m_identifier; + + /// Identifier name with PREFIX for calling @c PowerResourceManagerInterface. + const std::string m_prefixedIdentifier; + + /// Whether this resource is refCounted. + const bool m_isRefCounted; + + /// The PowerResourceId object used to call @c PowerResourceManagerInterface. + std::shared_ptr m_powerResourceId; + + /// Thread safety. + mutable std::mutex m_mutex; + + /// The current refCount. + uint64_t m_refCount; + + /// Level of the resource. Can be modified as different components may wish to obtain different levels. + sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel m_level; + + /// Whether whether the @c PowerResource is frozen. + bool m_isFrozen; + + /// The underlying @c PowerResourceManagerInterface + std::shared_ptr m_powerManager; +}; + +} // namespace power +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_POWER_POWERRESOURCE_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Power/WakeGuard.h b/AVSCommon/Utils/include/AVSCommon/Utils/Power/WakeGuard.h new file mode 100644 index 0000000000..569839c257 --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Power/WakeGuard.h @@ -0,0 +1,53 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#ifndef ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_POWER_WAKEGUARD_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_POWER_WAKEGUARD_H_ + +#include + +#include "AVSCommon/Utils/Power/PowerResource.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace power { + +/** + * A guard object which implements RAII semantics around @c PowerResource management. + */ +class WakeGuard { +public: + /** + * Constructor. + * + * @param powerResource The @c PowerResource to obtain. + */ + WakeGuard(std::shared_ptr powerResource); + + /// Destructor, releases the underlying resource. + ~WakeGuard(); + +private: + /// The @c PowerResource. + std::shared_ptr m_powerResource; +}; + +} // namespace power +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_POWER_WAKEGUARD_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/SDKVersion.h b/AVSCommon/Utils/include/AVSCommon/Utils/SDKVersion.h index f10aa17f3d..d1e511b79f 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.20.1"; + return "1.21.0"; } inline static int getMajorVersion() { @@ -37,11 +37,11 @@ inline static int getMajorVersion() { } inline static int getMinorVersion() { - return 20; + return 21; } inline static int getPatchVersion() { - return 1; + return 0; } } // namespace sdkVersion diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Threading/ConditionVariableWrapper.h b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/ConditionVariableWrapper.h new file mode 100644 index 0000000000..11f172cf45 --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/ConditionVariableWrapper.h @@ -0,0 +1,197 @@ +/* + * 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_THREADING_CONDITIONVARIABLEWRAPPER_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_THREADING_CONDITIONVARIABLEWRAPPER_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace threading { + +/** + * A wrapper around the standard std::condition_variable class that supports different power levels. + * Functionally it is the same as std::condition_variable. + * + * Methods not containing a predicate param are omitted. + */ +class ConditionVariableWrapper { +public: + /** + * Returns the tag for logging purposes. + */ + inline static std::string getTag() { + return "ConditionVariableWrapper"; + } + + /// Constructor. + ConditionVariableWrapper(); + + /** + * Notifies one thread waiting on the @c ConditionVariableWrapper. + */ + void notifyOne(); + + /** + * Notifies all threads waiting on the @c ConditionVariableWrapper. + */ + void notifyAll(); + + /** + * This function will freeze any associated thread @c PowerResource on the thread, ensuring that + * the system may enter a lower power state upon waiting. Upon unblocking, the thread @c PowerResource + * will be thawed. + * + * Refer to documentation for std::condition_variable for more information on standard behavior. + * + * @tparam Predicate The predicate to evaluate. + * + * @param lock The locked mutex. + * @param pred The predicate to evaluate. + */ + template + void wait(std::unique_lock& lock, Predicate pred); + + /** + * This function will freeze any associated thread @c PowerResource on the thread, ensuring that + * the system may enter a lower power state upon waiting. Upon unblocking, the thread @c PowerResource + * will be thawed. + * + * Refer to documentation for std::condition_variable for more information on standard behavior. + * + * @tparam Rep The data type used in representing the duration. + * @tparam Period The period used in representing the duration. + * @tparam Predicate The predicate to evaluate. + * + * @param lock The locked mutex. + * @param rel_time The time to wait for. + * @param pred The predicate to evaluate. + * @return The evaluation of pred. + */ + template + bool waitFor( + std::unique_lock& lock, + const std::chrono::duration& rel_time, + Predicate pred); + + /** + * This function will freeze any associated thread @c PowerResource on the thread, ensuring that + * the system may enter a lower power state upon waiting. Upon unblocking, the thread @c PowerResource + * will be thawed. + * + * Refer to documentation for std::condition_variable for more information on standard behavior. + * + * @tparam Clock The clock. + * @tparam Duration The duration. + * @tparam Pred The predicate to evaluate. + * + * @param lock The locked mutex. + * @param timeout_time The time to wait until. + * @param pred The predicate to evaluate. + * + * @return The evaluation of pred. + */ + template + bool waitUntil( + std::unique_lock& lock, + const std::chrono::time_point& timeout_time, + Predicate pred); + +private: + /** + * This function removes the template parameters to reduce amount of code expanded. + * + * @param lock The locked mutex. + * @param pred The transformed predicate. + */ + void waitInner(std::unique_lock& lock, std::function pred); + + /** + * This function removes the template parameters to reduce amount of code expanded. + * + * @param lock The locked mutex. + * @param relTime The time to wait for. + * @param pred The transformed predicate. + */ + bool waitForInner(std::unique_lock& lock, std::chrono::nanoseconds relTime, std::function pred); + + /// Identifier for instances. + static std::atomic g_id; + + /// Identifier. + const uint64_t m_id; + + /// Underlying std::condition_variable. + std::condition_variable m_cv; + + /** + * This is not the same mutex associated with m_cv, the underlying std::condition_variable. + * This is for protecting resources in this @c ConditionVariableWrapper class. + */ + std::mutex m_mutex; + + /** + * A @c PowerResource used to hold the @c PowerResource for notify_one() calls. This is necessary because + * the thread that is chosen to be unblocked by a notify_one() call is indeterministic from the SDKs perspective. + */ + std::shared_ptr m_notifyOnePowerResource; + + /** + * Track refCount on m_notifyOnPowerResource. This is to ensure we always have a corresponding release for each + * acquire. + */ + uint64_t m_notifyOnePowerResourceRefCount; + + /// Stores the set of thread resources frozen by this @c ConditionVariableWrapper. + std::unordered_set> m_frozenResources; +}; + +template +void ConditionVariableWrapper::wait(std::unique_lock& lock, Predicate pred) { + waitInner(lock, pred); +} + +template +bool ConditionVariableWrapper::waitFor( + std::unique_lock& lock, + const std::chrono::duration& rel_time, + Predicate pred) { + return waitForInner(lock, rel_time, pred); +}; + +template +bool ConditionVariableWrapper::waitUntil( + std::unique_lock& lock, + const std::chrono::time_point& timeout_time, + Predicate pred) { + return waitForInner(lock, timeout_time - Clock::now(), pred); +}; + +} // namespace threading +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_THREADING_CONDITIONVARIABLEWRAPPER_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Threading/Executor.h b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/Executor.h index 99c7e25174..4d2c0e507e 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Threading/Executor.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Threading/Executor.h @@ -27,6 +27,7 @@ #include #include "AVSCommon/Utils/Threading/TaskThread.h" +#include "AVSCommon/Utils/Power/PowerResource.h" namespace alexaClientSDK { namespace avsCommon { @@ -139,9 +140,15 @@ class Executor { /// A flag for whether or not the queue is expecting more tasks. std::atomic_bool m_shutdown; + /// A @c PowerResource. + std::shared_ptr m_powerResource; + /// The condition variable used to detect new job or timeout. std::condition_variable m_delayedCondition; + /// The id of this instance. + const uint64_t m_id; + /// The thread to execute tasks on. The thread must be declared last to be destructed first. TaskThread m_taskThread; }; @@ -227,6 +234,9 @@ auto Executor::pushTo(bool front, Task task, Args&&... args) -> std::future queueLock{m_queueMutex}; if (!m_shutdown) { restart = !m_threadRunning; + if (m_powerResource) { + m_powerResource->acquire(); + } m_queue.emplace(front ? m_queue.begin() : m_queue.end(), std::move(translated_task)); } else { using FutureType = decltype(task(args...)); diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/DistantFuture.h b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/DistantFuture.h new file mode 100644 index 0000000000..952c251864 --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/DistantFuture.h @@ -0,0 +1,44 @@ +/* + * 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_DISTANTFUTURE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_TIMING_DISTANTFUTURE_H_ + +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace timing { + +/** + * Get a consistent future @c steady_clock::time_point that we deem unreachable (110+ years), avoiding + * steady_clock::time_point::max() which results in an overflow due to a gcc bug. + * @see + * https://gcc-bugs.gcc.gnu.narkive.com/LBRgUQhD/bug-c-58931-new-condition-variable-wait-until-overflowed-by-large-time-point-steady-clock + * + * @return Get a consistent @c steady_clock::time_point that we deem unreachable. + */ +inline std::chrono::steady_clock::time_point getDistantFuture() { + static const auto distantFuture = std::chrono::steady_clock::now() + std::chrono::hours(1000000); + return distantFuture; +} + +} // namespace timing +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_TIMING_DISTANTFUTURE_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/Timer.h b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/Timer.h index 1e2859c2ae..bad0d3e6d4 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/Timer.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/Timer.h @@ -23,7 +23,10 @@ #include #include +#include "AVSCommon/Utils/Error/FinallyGuard.h" +#include "AVSCommon/AVS/Initialization/SDKPrimitivesProvider.h" #include "AVSCommon/Utils/Logger/LoggerUtils.h" +#include "AVSCommon/Utils/Timing/TimerDelegate.h" namespace alexaClientSDK { namespace avsCommon { @@ -62,8 +65,12 @@ class Timer { /** * Contructs a @c Timer. + * + * @param timerDelegateFactory A factory to create the @c TimerDelegate. */ - Timer(); + Timer( + std::shared_ptr timerDelegateFactory = + avs::initialization::SDKPrimitivesProvider::getInstance()->getTimerDelegateFactory()); /** * Destructs a @c Timer. @@ -212,26 +219,14 @@ class Timer { size_t maxCount, std::function task); - /// The condition variable used to wait for @c stop() or period timeouts. - std::condition_variable m_waitCondition; - - /// The mutex for @c m_waitCondition. - std::mutex m_waitMutex; + /// The mutex to protect the @c TimerDelegate. + mutable std::mutex m_mutex; - /// The mutex to join @c m_thread. - std::mutex m_joinMutex; + /// The @c TimerDelegateInterface which contains timer related logic. + std::unique_ptr m_timer; /// The thread to execute tasks on. std::thread m_thread; - - /// Flag which indicates that a @c Timer is active. - std::atomic m_running; - - /** - * Flag which requests that the active @c Timer be stopped. @c m_waitMutex must be locked when modifying this - * variable. - */ - bool m_stopping; }; template @@ -251,17 +246,12 @@ bool Timer::start( return false; } - // Can't start if already running. + // Don't start if already running. if (!activate()) { logger::acsdkError(logger::LogEntry(Timer::getTag(), "startFailed").d("reason", "timerAlreadyActive")); return false; } - // Join old timer thread (if any). - if (m_thread.joinable()) { - m_thread.join(); - } - // Remove arguments from the task's type by binding the arguments to the task. using BoundTaskType = decltype(std::bind(std::forward(task), std::forward(args)...)); auto boundTask = std::make_shared(std::bind(std::forward(task), std::forward(args)...)); @@ -270,8 +260,7 @@ bool Timer::start( auto translatedTask = [boundTask]() { boundTask->operator()(); }; // Kick off the new timer thread. - m_thread = std::thread{ - std::bind(&Timer::callTask, this, delay, period, periodType, maxCount, translatedTask)}; + callTask(delay, period, periodType, maxCount, translatedTask); return true; } @@ -289,18 +278,13 @@ bool Timer::start( template auto Timer::start(const std::chrono::duration& delay, Task task, Args&&... args) -> std::future { - // Can't start if already running. + // Don't start if already running. if (!activate()) { logger::acsdkError(logger::LogEntry(Timer::getTag(), "startFailed").d("reason", "timerAlreadyActive")); using FutureType = decltype(task(args...)); return std::future(); } - // Join old timer thread (if any). - if (m_thread.joinable()) { - m_thread.join(); - } - // Remove arguments from the task's type by binding the arguments to the task. auto boundTask = std::bind(std::forward(task), std::forward(args)...); @@ -317,8 +301,7 @@ auto Timer::start(const std::chrono::duration& delay, Task task, Ar // Kick off the new timer thread. static const size_t once = 1; - m_thread = std::thread{ - std::bind(&Timer::callTask, this, delay, delay, PeriodType::ABSOLUTE, once, translatedTask)}; + callTask(delay, delay, PeriodType::ABSOLUTE, once, translatedTask); return packagedTask->get_future(); } @@ -330,51 +313,28 @@ void Timer::callTask( PeriodType periodType, size_t maxCount, std::function task) { - // Timepoint to measure delay/period against. - auto now = std::chrono::steady_clock::now(); - - // Flag indicating whether we've drifted off schedule. - bool offSchedule = false; - - for (size_t count = 0; maxCount == FOREVER || count < maxCount; ++count) { - auto waitTime = (0 == count) ? delay : period; - { - std::unique_lock lock(m_waitMutex); - - // Wait for stop() or a delay/period to elapse. - if (m_waitCondition.wait_until(lock, now + waitTime, [this]() { return m_stopping; })) { - m_stopping = false; - m_running = false; - return; - } - } - - switch (periodType) { - case PeriodType::ABSOLUTE: - // Update our estimate of where we should be after the delay. - now += waitTime; - - // Run the task if we're still on schedule. - if (!offSchedule) { - task(); - } - - // If the task runtime put us off schedule, skip the next task run. - if (now + period < std::chrono::steady_clock::now()) { - offSchedule = true; - } else { - offSchedule = false; - } - break; - - case PeriodType::RELATIVE: - task(); - now = std::chrono::steady_clock::now(); - break; - } + if (!m_timer) { + logger::acsdkError(logger::LogEntry(Timer::getTag(), "callTaskFailed").d("reason", "nullTimerDelegate")); + return; } - m_stopping = false; - m_running = false; + + auto delegatePeriodType = sdkInterfaces::timing::TimerDelegateInterface::PeriodType::ABSOLUTE; + + switch (periodType) { + case PeriodType::ABSOLUTE: + delegatePeriodType = sdkInterfaces::timing::TimerDelegateInterface::PeriodType::ABSOLUTE; + break; + case PeriodType::RELATIVE: + delegatePeriodType = sdkInterfaces::timing::TimerDelegateInterface::PeriodType::RELATIVE; + break; + } + + m_timer->start( + std::chrono::duration_cast(delay), + std::chrono::duration_cast(period), + delegatePeriodType, + maxCount, + task); } } // namespace timing diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/TimerDelegate.h b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/TimerDelegate.h new file mode 100644 index 0000000000..79602e7807 --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/TimerDelegate.h @@ -0,0 +1,100 @@ +/* + * 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_TIMERDELEGATE_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_TIMING_TIMERDELEGATE_H_ + +#include +#include +#include + +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace timing { + +class TimerDelegate : public sdkInterfaces::timing::TimerDelegateInterface { +public: + /// @name TimerDelegateInterface Functions + /// @{ + void start( + std::chrono::nanoseconds delay, + std::chrono::nanoseconds period, + PeriodType periodType, + size_t maxCount, + std::function task) override; + void stop() override; + bool activate() override; + bool isActive() const override; + /// @} + + /// Constructor. + TimerDelegate(); + +private: + /** + * Main timer loop. + * + * @param delay The non-negative time to wait before making the first @c task call. + * @param period The non-negative time to wait between subsequent @c task calls. + * @param periodType The type of period to use when making subsequent task calls. + * @param maxCount The desired number of times to call task. + * @param task A callable type representing a task. + */ + void timerLoop( + std::chrono::nanoseconds delay, + std::chrono::nanoseconds period, + PeriodType periodType, + size_t maxCount, + std::function task); + + /// Cleanup logic which waits for the thread to join if possible. @c m_callMutex must be held before calling. + void cleanupLocked(); + + /// Internal logic that activates this @c TimerDelegate instance. + bool activateLocked(); + + /// The condition variable used to wait for @c stop() or period timeouts. + std::condition_variable m_waitCondition; + + /// The mutex for synchronization with the std::condition_variable. + mutable std::mutex m_waitMutex; + + /// The mutex for synchronizing calls into TimerDelegate. + mutable std::mutex m_callMutex; + + /// Flag which indicates that a @c Timer is active. + bool m_running; + + /** + * Flag which requests that the active @c Timer be stopped. @c m_waitMutex must be locked when modifying this + * variable. + */ + bool m_stopping; + + /** + * The thread which this runs on. + */ + std::thread m_thread; +}; + +} // namespace timing +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_TIMING_TIMERDELEGATE_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/Timing/TimerDelegateFactory.h b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/TimerDelegateFactory.h new file mode 100644 index 0000000000..0094f83af8 --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/Timing/TimerDelegateFactory.h @@ -0,0 +1,41 @@ +/* + * 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_TIMERDELEGATEFACTORY_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_TIMING_TIMERDELEGATEFACTORY_H_ + +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace timing { + +class TimerDelegateFactory : public avsCommon::sdkInterfaces::timing::TimerDelegateFactoryInterface { +public: + /// @name TimerDelegateFactoryInterface Functions + /// @{ + bool supportsLowPowerMode() override; + std::unique_ptr getTimerDelegate() override; + /// @} +}; + +} // namespace timing +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_TIMING_TIMERDELEGATEFACTORY_H_ diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/UUIDGeneration/UUIDGeneration.h b/AVSCommon/Utils/include/AVSCommon/Utils/UUIDGeneration/UUIDGeneration.h index 90cba88567..ff5ca5ca92 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/UUIDGeneration/UUIDGeneration.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/UUIDGeneration/UUIDGeneration.h @@ -16,13 +16,21 @@ #ifndef ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_UUIDGENERATION_UUIDGENERATION_H_ #define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_UUIDGENERATION_UUIDGENERATION_H_ +#include #include +#include namespace alexaClientSDK { namespace avsCommon { namespace utils { namespace uuidGeneration { +/** + * Set the customized function to read entropy value instead of the default. + * @param func Customized function used to read entropy value. + */ +void setEntropyReader(std::function func); + /** * Generates a variant 1, version 4 universally unique identifier (UUID) consisting of 32 hexadecimal digits. * The UUID generated is of the format xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx where M indicates the version, and the two @@ -40,9 +48,20 @@ const std::string generateUUID(); * Setting a salt will cause the next UUID to be generated with a new seed. * * @param newSalt the salt to use + * @deprecated */ void setSalt(const std::string& newSalt); +/** + * Allows caller to add extra seeds to generate uuid. Good seeds should be random values, + * such as the time between two consecutive user interactions. UUIDGeneration uses one + * random_device value, lower 32bits of current time, time between two invocations, and + * lower 32bits memory address of temporary variable to feed seeds for random number generator. + * + * @param seeds extra seeds from a random source. + */ +void addSeeds(const std::vector& seeds); + } // namespace uuidGeneration } // namespace utils } // namespace avsCommon diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/WaitEvent.h b/AVSCommon/Utils/include/AVSCommon/Utils/WaitEvent.h index 29f2c4eeca..6b581af2c4 100644 --- a/AVSCommon/Utils/include/AVSCommon/Utils/WaitEvent.h +++ b/AVSCommon/Utils/include/AVSCommon/Utils/WaitEvent.h @@ -16,8 +16,8 @@ #define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_WAITEVENT_H_ #include -#include #include +#include namespace alexaClientSDK { namespace avsCommon { @@ -59,7 +59,7 @@ class WaitEvent { private: /// The condition variable used to wake up the thread that is waiting. - std::condition_variable m_condition; + avsCommon::utils::threading::ConditionVariableWrapper m_condition; /// The mutex used to lock the condition. std::mutex m_mutex; diff --git a/AVSCommon/Utils/include/AVSCommon/Utils/WavUtils.h b/AVSCommon/Utils/include/AVSCommon/Utils/WavUtils.h new file mode 100644 index 0000000000..0e6bcecc35 --- /dev/null +++ b/AVSCommon/Utils/include/AVSCommon/Utils/WavUtils.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_UTILS_INCLUDE_AVSCOMMON_UTILS_WAVUTILS_H_ +#define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_WAVUTILS_H_ + +#include +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { + +// WAV definitions +constexpr const char* ID_RIFF = "RIFF"; +constexpr const char* ID_WAVE = "WAVE"; +constexpr const char* ID_FMT = "fmt "; +constexpr const char* ID_DATA = "data"; +constexpr uint32_t PCM_SUBCHUNK_SZ = 16; + +// PCM definitions +constexpr uint16_t FORMAT_PCM = 1; +constexpr uint16_t BITS_PER_BYTE = 8; +constexpr uint16_t PLAY_BITS_PER_SAMPLE = 16; + +// FLOAT definitions +constexpr uint16_t FORMAT_IEEE_FLOAT = 3; +constexpr uint32_t FLOAT_SUBCHUNK_SZ = 18; +constexpr uint32_t FACT_CHUNK_SZ = 4; +constexpr const char* ID_FACT = "fact"; + +// WAV Header Sizes +constexpr unsigned int PCM_HEADER_SIZE = 44; +constexpr unsigned int NON_PCM_HEADER_SIZE = 58; + +// WAV Header Offsets +constexpr unsigned int RIFF_ID_OFFSET = 0; +constexpr unsigned int RIFF_SZ_OFFSET = 4; +constexpr unsigned int RIFF_FMT_OFFSET = 8; +constexpr unsigned int FMT_ID_OFFSET = 12; +constexpr unsigned int FMT_SZ_OFFSET = 16; +constexpr unsigned int AUDIO_FORMAT_OFFSET = 20; +constexpr unsigned int NUM_CHANNELS_OFFSET = 22; +constexpr unsigned int SAMPLE_RATE_OFFSET = 24; +constexpr unsigned int BYTE_RATE_OFFSET = 28; +constexpr unsigned int BLOCK_ALIGN_OFFSET = 32; +constexpr unsigned int BITS_PER_SAMPLE_OFFSET = 34; +constexpr unsigned int DATA_ID_OFFSET = 36; +constexpr unsigned int DATA_SZ_OFFSET = 40; + +// Non-PCM Fields +constexpr unsigned int CB_SZ_OFFSET = 36; +constexpr unsigned int FACT_ID_OFFSET = 38; +constexpr unsigned int FACT_SZ_OFFSET = 42; +constexpr unsigned int FACT_SAMPLE_LEN_OFFSET = 46; + +// OFFSET for Non-PCM fields +constexpr unsigned int NON_PCM_OFFSET = 14; + +using ByteVector = std::vector; + +/** + * Structure defining the WavHeader contents. + */ +struct WavHeader { + uint32_t riffId; + uint32_t riffSz; + uint32_t riffFmt; + uint32_t fmtId; + uint32_t fmtSz; + uint16_t audioFormat; + uint16_t numChannels; + uint32_t sampleRate; + uint32_t byteRate; + uint16_t blockAlign; + uint16_t bitsPerSample; + uint16_t cbSz; + uint32_t factId; + uint32_t factSz; + uint32_t factSampleLen; + uint32_t dataId; + uint32_t dataSz; +}; + +/** + * Generates a WAVE header. + * + * @param bytesPerSample The number of bytes per sample. + * @param channels The number of channels in the audio data. + * @param rate The sample rate of the audio data, in blocks per second. + * @param totalDuration The total duration of the audio data. + * @param isPCM Bool indicating if header to generate is PCM format or Non-PCM. + * + * @return The ByteVector of the generated WAVE header. + */ +ByteVector generateWavHeader( + unsigned int bytesPerSample, + unsigned int channels, + unsigned int rate, + std::chrono::milliseconds totalDuration, + bool isPCM = true); + +/** + * Reads a .wav file and seperates the audio data from the header data. + * + * @param absoluteFilePath The path to a file to retrieve the WAVE header from. + * @param audioBuffer The pointer to the audioBuffer vector. + * @param wavHeader The WavHeader. + * @param isPCM Bool indicating if header to generate is PCM format or Non-PCM. + * + * @return true if successful, else false. + */ +bool readWAVFile( + const std::string& absoluteFilePath, + std::vector* audioBuffer, + WavHeader& wavHeader, + bool isPCM = true); + +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_WAVUTILS_H_ diff --git a/AVSCommon/Utils/src/Executor.cpp b/AVSCommon/Utils/src/Executor.cpp index 51a052b0d2..ac14bfc82e 100644 --- a/AVSCommon/Utils/src/Executor.cpp +++ b/AVSCommon/Utils/src/Executor.cpp @@ -14,6 +14,7 @@ */ #include "AVSCommon/Utils/Memory/Memory.h" +#include "AVSCommon/Utils/Power/PowerMonitor.h" #include "AVSCommon/Utils/Threading/Executor.h" namespace alexaClientSDK { @@ -21,6 +22,9 @@ namespace avsCommon { namespace utils { namespace threading { +/// An id for identifying instances. +static std::atomic g_id{0}; + Executor::~Executor() { shutdown(); } @@ -28,7 +32,9 @@ Executor::~Executor() { Executor::Executor(const std::chrono::milliseconds& delayExit) : m_threadRunning{false}, m_timeout{delayExit}, - m_shutdown{false} { + m_shutdown{false}, + m_id{g_id++} { + m_powerResource = power::PowerMonitor::getInstance()->createLocalPowerResource("Executor:" + std::to_string(m_id)); } void Executor::waitForSubmittedTasks() { @@ -57,6 +63,7 @@ std::function Executor::pop() { bool Executor::hasNext() { std::unique_lock lock{m_queueMutex}; + m_delayedCondition.wait_for(lock, m_timeout, [this] { return !m_queue.empty() || m_shutdown; }); m_threadRunning = !m_queue.empty(); return m_threadRunning; @@ -68,6 +75,14 @@ bool Executor::runNext() { task(); } + if (m_powerResource) { + m_powerResource->release(); + } + + // It is acceptable that we enter LPM before + // the wait. TaskThread will still wait the intended m_timeout relative to the system + // not in LPM. + return hasNext(); } diff --git a/AVSCommon/Utils/src/LibcurlUtils/CurlEasyHandleWrapper.cpp b/AVSCommon/Utils/src/LibcurlUtils/CurlEasyHandleWrapper.cpp index fdfb346bc2..037bf81454 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/CurlEasyHandleWrapper.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/CurlEasyHandleWrapper.cpp @@ -354,6 +354,28 @@ long CurlEasyHandleWrapper::getHTTPResponseCode() { return 0; } +std::string CurlEasyHandleWrapper::getEffectiveUrl() { + std::string effectiveUrl; + if (isValid()) { + char* temp = nullptr; + auto status = curl_easy_getinfo(m_handle, CURLINFO_EFFECTIVE_URL, &temp); + if (status != CURLE_OK) { + ACSDK_ERROR(LX("getEffectiveURLFailed").d("reason", "curlEasyGetinfoFailed").d("status", status)); + return effectiveUrl; + } + + if (temp) { + effectiveUrl = temp; + ACSDK_DEBUG7(LX(__func__).d("effectiveURL", effectiveUrl)); + } + + } else { + ACSDK_ERROR(LX("getEffectiveURLFailed").d("reason", "notValid")); + } + + return effectiveUrl; +} + CURLcode CurlEasyHandleWrapper::perform() { if (isValid()) { return curl_easy_perform(m_handle); diff --git a/AVSCommon/Utils/src/LibcurlUtils/HTTPContentFetcherFactory.cpp b/AVSCommon/Utils/src/LibcurlUtils/HTTPContentFetcherFactory.cpp index 7324ed9141..6cd909c27b 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/HTTPContentFetcherFactory.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/HTTPContentFetcherFactory.cpp @@ -33,6 +33,11 @@ static const std::string TAG("HTTPContentFetcherFactory"); */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) +std::shared_ptr HTTPContentFetcherFactory:: + createHTTPContentFetcherInterfaceFactoryInterface() { + return std::make_shared(); +} + std::unique_ptr HTTPContentFetcherFactory::create( const std::string& url) { ACSDK_DEBUG9(LX(__func__).sensitive("URL", url).m("Creating a new http content fetcher")); diff --git a/AVSCommon/Utils/src/LibcurlUtils/LibCurlHttpContentFetcher.cpp b/AVSCommon/Utils/src/LibcurlUtils/LibCurlHttpContentFetcher.cpp index 406599c2d5..a5b8cfd738 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/LibCurlHttpContentFetcher.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/LibCurlHttpContentFetcher.cpp @@ -204,6 +204,7 @@ size_t LibCurlHttpContentFetcher::noopCallback(char* data, size_t size, size_t n LibCurlHttpContentFetcher::LibCurlHttpContentFetcher(const std::string& url) : m_state{HTTPContentFetcherInterface::State::INITIALIZED}, m_url{url}, + m_effectiveUrl{url}, m_currentContentReceivedLength{0}, m_totalContentReceivedLength{0}, m_done{false}, @@ -373,6 +374,7 @@ std::unique_ptr LibCurlHttpContentFetcher::getCon ACSDK_ERROR(LX("curlEasyGetInfoFailed").d("error", curl_easy_strerror(curlReturnValue))); break; } + if (HTTPResponseCode::HTTP_RESPONSE_CODE_UNDEFINED != finalResponseCode && !isRedirect(finalResponseCode)) { ACSDK_DEBUG9(LX("getContent").d("responseCode", finalResponseCode).sensitive("url", m_url)); @@ -398,6 +400,8 @@ std::unique_ptr LibCurlHttpContentFetcher::getCon } } + updateEffectiveURL(); + // Free custom headers. curl_slist_free_all(headerList); @@ -493,6 +497,8 @@ std::unique_ptr LibCurlHttpContentFetcher::getCon } } + updateEffectiveURL(); + /* * If the writer was created locally, its job is done and can be safely closed. */ @@ -682,6 +688,16 @@ bool LibCurlHttpContentFetcher::waitingForBodyRequest() { return (State::INITIALIZED == state) || (State::FETCHING_HEADER == state) || (State::HEADER_DONE == state); } +void LibCurlHttpContentFetcher::updateEffectiveURL() { + std::lock_guard lock{m_effectiveUrlMutex}; + m_effectiveUrl = m_curlWrapper.getEffectiveUrl(); +} + +std::string LibCurlHttpContentFetcher::getEffectiveUrl() const { + std::lock_guard lock{m_effectiveUrlMutex}; + return m_effectiveUrl; +} + } // namespace libcurlUtils } // namespace utils } // namespace avsCommon diff --git a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2ConnectionFactory.cpp b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2ConnectionFactory.cpp index e691dd7e7f..143516fda7 100644 --- a/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2ConnectionFactory.cpp +++ b/AVSCommon/Utils/src/LibcurlUtils/LibcurlHTTP2ConnectionFactory.cpp @@ -33,6 +33,11 @@ static const std::string TAG("LibcurlHTTP2ConnectionFactory"); */ #define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) +std::shared_ptr LibcurlHTTP2ConnectionFactory:: + createHTTP2ConnectionFactoryInterface() { + return std::make_shared(); +} + std::shared_ptr LibcurlHTTP2ConnectionFactory:: createHTTP2Connection() { ACSDK_DEBUG5(LX(__func__)); diff --git a/AVSCommon/Utils/src/Logger/Logger.cpp b/AVSCommon/Utils/src/Logger/Logger.cpp index 1145dc34d4..3809d28ab4 100644 --- a/AVSCommon/Utils/src/Logger/Logger.cpp +++ b/AVSCommon/Utils/src/Logger/Logger.cpp @@ -75,7 +75,9 @@ void Logger::setLevel(Level level) { .d("level", m_level) .m("\n" "\nWARNING: By default DEBUG logs are compiled out of RELEASE builds." - "\nRebuild with the cmake parameter -DCMAKE_BUILD_TYPE=DEBUG to enable debug logs." + "\nTo enable debug logs, rebuild in DEBUG mode by using the cmake parameter " + "-DCMAKE_BUILD_TYPE=DEBUG or (if you *really* want debug logs in a RELEASE build) use the cmake " + "parameter -DACSDK_DEBUG_LOG=ON." "\n")); } #endif diff --git a/AVSCommon/Utils/src/MediaPlayer/PlaybackContext.cpp b/AVSCommon/Utils/src/MediaPlayer/PlaybackContext.cpp index 331e0c82cc..f72b27cb7e 100644 --- a/AVSCommon/Utils/src/MediaPlayer/PlaybackContext.cpp +++ b/AVSCommon/Utils/src/MediaPlayer/PlaybackContext.cpp @@ -40,11 +40,16 @@ const std::string PlaybackContext::HTTP_AUDIOSEGMENT_HEADERS = "audioSegment"; const std::string PlaybackContext::HTTP_ALL_HEADERS = "all"; static const std::string AUTHORIZATION = "Authorization"; static const std::string ALLOWED_PREFIX = "x-"; +static const std::string COOKIE = "Cookie"; static const unsigned int MIN_KEY_LENGTH = 3; static const unsigned int MAX_KEY_LENGTH = 256; static const unsigned int MAX_VALUE_LENGTH = 4096; static const unsigned int MAX_ENTRIES_PER_CONFIG = 20; +bool validateIfNotMalicious(const std::string& header) { + return (!header.empty() && header.find("\r") == std::string::npos && header.find("\n") == std::string::npos); +} + /** * Helper function to validate the headers. * @@ -54,9 +59,11 @@ static const unsigned int MAX_ENTRIES_PER_CONFIG = 20; static bool validatePlaybackContextHeadersInternal(HeaderConfig* headerConfig) { bool foundInvalidHeaders = false; for (auto entry = headerConfig->begin(); entry != headerConfig->end();) { - if ((entry->first.find(ALLOWED_PREFIX) == 0 && entry->first.length() >= MIN_KEY_LENGTH && - entry->first.length() <= MAX_KEY_LENGTH && entry->second.length() <= MAX_VALUE_LENGTH) || - (entry->first.compare(AUTHORIZATION) == 0 && entry->second.length() <= MAX_VALUE_LENGTH)) { + if (((entry->first.find(ALLOWED_PREFIX) == 0 && entry->first.length() >= MIN_KEY_LENGTH && + entry->first.length() <= MAX_KEY_LENGTH && entry->second.length() <= MAX_VALUE_LENGTH) || + (entry->first.compare(AUTHORIZATION) == 0 && entry->second.length() <= MAX_VALUE_LENGTH) || + (entry->first.compare(COOKIE) == 0 && entry->second.length() <= MAX_VALUE_LENGTH)) && + (validateIfNotMalicious(entry->first) && validateIfNotMalicious(entry->second))) { entry++; } else { entry = headerConfig->erase(entry); diff --git a/AVSCommon/Utils/src/MediaPlayer/PooledMediaPlayerFactory.cpp b/AVSCommon/Utils/src/MediaPlayer/PooledMediaPlayerFactory.cpp index cdd43a3125..435866fb30 100644 --- a/AVSCommon/Utils/src/MediaPlayer/PooledMediaPlayerFactory.cpp +++ b/AVSCommon/Utils/src/MediaPlayer/PooledMediaPlayerFactory.cpp @@ -44,12 +44,12 @@ std::unique_ptr PooledMediaPlayerFactory::create( } PooledMediaPlayerFactory::PooledMediaPlayerFactory( - const std::vector>& pool, + const std::vector>& mediaPlayerPool, const Fingerprint& fingerprint) : // Parenthesis are used for initializing @c m_fingerprint to work-around a bug in the C++ specification. see: // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1288 m_fingerprint(fingerprint) { - m_availablePlayerPool.insert(m_availablePlayerPool.end(), pool.begin(), pool.end()); + m_availablePlayerPool.insert(m_availablePlayerPool.end(), mediaPlayerPool.begin(), mediaPlayerPool.end()); } PooledMediaPlayerFactory::~PooledMediaPlayerFactory() { diff --git a/AVSCommon/Utils/src/MediaPlayer/PooledMediaResourceProvider.cpp b/AVSCommon/Utils/src/MediaPlayer/PooledMediaResourceProvider.cpp new file mode 100644 index 0000000000..06858a3020 --- /dev/null +++ b/AVSCommon/Utils/src/MediaPlayer/PooledMediaResourceProvider.cpp @@ -0,0 +1,117 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "AVSCommon/Utils/MediaPlayer/PooledMediaPlayerFactory.h" +#include "AVSCommon/Utils/MediaPlayer/PooledMediaResourceProvider.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace mediaPlayer { + +using namespace avsCommon::sdkInterfaces; + +static const std::string TAG("PooledMediaResourceProvider"); + +#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) + +std::shared_ptr PooledMediaResourceProvider:: + adaptMediaPlayerFactoryInterface( + std::unique_ptr mediaPlayerFactory, + std::vector> speakers) { + if (!mediaPlayerFactory) { + ACSDK_ERROR(LX("adaptMediaPlayerFactoryInterfaceFailed").m("invalid mediaPlayerFactory")); + return nullptr; + } + return std::shared_ptr( + new PooledMediaResourceProvider(std::move(mediaPlayerFactory), speakers)); +} + +std::shared_ptr PooledMediaResourceProvider:: + createPooledMediaResourceProviderInterface( + std::vector> mediaPlayers, + std::vector> speakers, + const avsCommon::utils::mediaPlayer::Fingerprint& fingerprint) { + auto mediaPlayerFactory = alexaClientSDK::mediaPlayer::PooledMediaPlayerFactory::create(mediaPlayers, fingerprint); + if (!mediaPlayerFactory) { + ACSDK_ERROR(LX("createPooledMediaResourceProviderInterfaceFailed").m("invalid mediaPlayerFactory")); + return nullptr; + } + + if (speakers.empty()) { + ACSDK_ERROR(LX("createPooledMediaResourceProviderInterfaceFailed").m("empty speakers")); + return nullptr; + } + for (const auto& speaker : speakers) { + if (!speaker) { + ACSDK_ERROR(LX("createPooledMediaResourceProviderInterfaceFailed").m("nullptr in speakers")); + return nullptr; + } + } + + return std::shared_ptr( + new PooledMediaResourceProvider(std::move(mediaPlayerFactory), speakers)); +} + +PooledMediaResourceProvider::~PooledMediaResourceProvider() { + m_factory.reset(); + m_speakers.clear(); +} + +avsCommon::utils::mediaPlayer::Fingerprint PooledMediaResourceProvider::getFingerprint() { + return m_factory->getFingerprint(); +} + +std::shared_ptr PooledMediaResourceProvider::acquireMediaPlayer() { + return m_factory->acquireMediaPlayer(); +} + +bool PooledMediaResourceProvider::releaseMediaPlayer( + std::shared_ptr mediaPlayer) { + return m_factory->releaseMediaPlayer(mediaPlayer); +} + +bool PooledMediaResourceProvider::isMediaPlayerAvailable() { + return m_factory->isMediaPlayerAvailable(); +} + +void PooledMediaResourceProvider::addObserver( + std::shared_ptr observer) { + m_factory->addObserver(observer); +} + +void PooledMediaResourceProvider::removeObserver( + std::shared_ptr observer) { + m_factory->removeObserver(observer); +} + +std::vector> PooledMediaResourceProvider:: + getSpeakers() const { + return m_speakers; +} + +PooledMediaResourceProvider::PooledMediaResourceProvider( + std::unique_ptr mediaPlayerFactory, + std::vector> speakers) : + m_factory{std::move(mediaPlayerFactory)}, + m_speakers(speakers) { +} + +} // namespace mediaPlayer +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/src/Metrics/UplData.cpp b/AVSCommon/Utils/src/Metrics/UplData.cpp new file mode 100644 index 0000000000..846547e8ca --- /dev/null +++ b/AVSCommon/Utils/src/Metrics/UplData.cpp @@ -0,0 +1,106 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "AVSCommon/Utils/Metrics/UplData.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace metrics { + +/// String to identify log entries originating from this file. +static const std::string TAG("UplData"); + +/** + * 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) + +void UplData::addTimepoint(const std::string& name, const UplTimePoint& timepoint) { + auto key = m_uplTimePointsMap.find(name); + if (key != m_uplTimePointsMap.end()) { + ACSDK_WARN(LX(__func__).m("New timepoint already exists in UplTimePointsMap").d("name", name)); + } + m_uplTimePointsMap[name] = timepoint; +} + +UplData::UplTimePoint UplData::getTimepoint(const std::string& name) const { + auto key = m_uplTimePointsMap.find(name); + if (key == m_uplTimePointsMap.end()) { + return UplTimePoint(); + } + return key->second; +} + +void UplData::addParseCompleteTimepoint(const std::string& directiveId, const UplTimePoint& timepoint) { + auto key = m_messageIdToParseCompleteMap.find(directiveId); + if (key != m_messageIdToParseCompleteMap.end()) { + ACSDK_WARN(LX(__func__) + .m("New timepoint already exists in messageIdToParseCompleteMap") + .d("directiveId", directiveId)); + } + m_messageIdToParseCompleteMap[directiveId] = timepoint; +} + +UplData::UplTimePoint UplData::getParseCompleteTimepoint(const std::string& directiveId) const { + auto key = m_messageIdToParseCompleteMap.find(directiveId); + if (key == m_messageIdToParseCompleteMap.end()) { + return UplTimePoint(); + } + return key->second; +} + +void UplData::addDirectiveDispatchedTimepoint(const std::string& directiveId, const UplTimePoint& timepoint) { + auto key = m_messageIdToDirectiveDispatchedMap.find(directiveId); + if (key != m_messageIdToDirectiveDispatchedMap.end()) { + ACSDK_WARN(LX(__func__) + .m("New timepoint already exists in messageIdToDirectiveDispatchedMap") + .d("directiveId", directiveId)); + } + m_messageIdToDirectiveDispatchedMap[directiveId] = timepoint; +} + +UplData::UplTimePoint UplData::getDirectiveDispatchedTimepoint(const std::string& directiveId) const { + auto key = m_messageIdToDirectiveDispatchedMap.find(directiveId); + if (key == m_messageIdToDirectiveDispatchedMap.end()) { + return UplTimePoint(); + } + return key->second; +} + +void UplData::addStringData(const std::string& name, const std::string& data) { + auto key = m_otherData.find(name); + if (key != m_otherData.end()) { + ACSDK_WARN(LX(__func__).m("New string data already exists in otherData").d("name", name)); + } + m_otherData[name] = data; +} + +std::string UplData::getStringData(const std::string& name) const { + auto key = m_otherData.find(name); + if (key == m_otherData.end()) { + return std::string(); + } + return key->second; +} + +} // namespace metrics +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK \ No newline at end of file diff --git a/AVSCommon/Utils/src/Network/InternetConnectionMonitor.cpp b/AVSCommon/Utils/src/Network/InternetConnectionMonitor.cpp index f94e8c385a..50b5426687 100644 --- a/AVSCommon/Utils/src/Network/InternetConnectionMonitor.cpp +++ b/AVSCommon/Utils/src/Network/InternetConnectionMonitor.cpp @@ -53,10 +53,20 @@ static const std::string VALIDATION_STRING = "81ce4465-7167-4dcb-835b-dcc9e44c11 /// Process attachment ID prefix static const std::string PROCESS_ATTACHMENT_ID_PREFIX = "download:"; +std::shared_ptr InternetConnectionMonitor:: + createInternetConnectionMonitorInterface( + const std::shared_ptr& contentFetcherFactory) { + if (!contentFetcherFactory) { + ACSDK_ERROR(LX("createInternetConnectionMonitorInterfaceFailed").d("reason", "nullContentFetcherFactory")); + return nullptr; + } + return std::shared_ptr(new InternetConnectionMonitor(contentFetcherFactory)); +} + std::unique_ptr InternetConnectionMonitor::create( std::shared_ptr contentFetcherFactory) { if (!contentFetcherFactory) { - ACSDK_ERROR(LX("createFailed").d("reason", "contentFetcherFactory was nullptr")); + ACSDK_ERROR(LX("createFailed").d("reason", "nullContentFetcherFactory")); return nullptr; } diff --git a/AVSCommon/Utils/src/Power/AggregatedPowerResourceManager.cpp b/AVSCommon/Utils/src/Power/AggregatedPowerResourceManager.cpp new file mode 100644 index 0000000000..b61f0b9c97 --- /dev/null +++ b/AVSCommon/Utils/src/Power/AggregatedPowerResourceManager.cpp @@ -0,0 +1,233 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace power { + +using namespace avsCommon::sdkInterfaces; + +/// String to identify log entries originating from this file. +static const std::string TAG("AggregatedPowerResourceManager"); + +/// Prefix to uniquely identify the resource. +static const std::string PREFIX = "ACSDK_"; + +/** + * 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) + +AggregatedPowerResourceManager::PowerResourceInfo::PowerResourceInfo( + bool isRefCounted, + avsCommon::sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel level) : + isRefCounted{isRefCounted}, + level{level}, + refCount{0} { +} + +std::shared_ptr AggregatedPowerResourceManager::create( + std::shared_ptr powerResourceManager) { + ACSDK_DEBUG5(LX(__func__)); + if (!powerResourceManager) { + ACSDK_ERROR(LX(__func__).d("reason", "nullPowerResourceManager")); + return nullptr; + } else { + return std::shared_ptr( + new AggregatedPowerResourceManager(powerResourceManager)); + } +} + +AggregatedPowerResourceManager::~AggregatedPowerResourceManager() { + std::lock_guard lock(m_mutex); + for (auto it : m_aggregatedPowerResources) { + auto id = it.second; + m_appPowerResourceManager->close(id); + } +} + +AggregatedPowerResourceManager::AggregatedPowerResourceManager( + std::shared_ptr powerResourceManager) : + m_appPowerResourceManager{powerResourceManager} { +} + +void AggregatedPowerResourceManager::acquirePowerResource( + const std::string& component, + const PowerResourceLevel level) { + ACSDK_DEBUG9(LX(__func__)); + + std::lock_guard lock(m_mutex); + m_appPowerResourceManager->acquirePowerResource(component, level); +} + +void AggregatedPowerResourceManager::releasePowerResource(const std::string& component) { + ACSDK_DEBUG9(LX(__func__)); + + std::lock_guard lock(m_mutex); + m_appPowerResourceManager->releasePowerResource(component); +}; + +bool AggregatedPowerResourceManager::isPowerResourceAcquired(const std::string& component) { + ACSDK_DEBUG9(LX(__func__)); + + std::lock_guard lock(m_mutex); + return m_appPowerResourceManager->isPowerResourceAcquired(component); +} + +std::shared_ptr AggregatedPowerResourceManager:: + getAggregatedPowerResourceIdLocked(const PowerResourceManagerInterface::PowerResourceLevel level) { + ACSDK_DEBUG9(LX(__func__)); + + auto it = m_aggregatedPowerResources.find(level); + if (m_aggregatedPowerResources.end() != it) { + return it->second; + } + + ACSDK_DEBUG0(LX(__func__).d("reason", "generatingNewAggregateResource").d("level", level)); + + auto aggregatedPowerResourceId = + m_appPowerResourceManager->create(PREFIX + powerResourceLevelToString(level), true, level); + + m_aggregatedPowerResources.insert({level, aggregatedPowerResourceId}); + return aggregatedPowerResourceId; +} + +std::shared_ptr AggregatedPowerResourceManager::create( + const std::string& resourceId, + bool isRefCounted, + const PowerResourceManagerInterface::PowerResourceLevel level) { + ACSDK_DEBUG9(LX(__func__).d("id", resourceId)); + + std::lock_guard lock(m_mutex); + + if (m_ids.count(resourceId) != 0) { + ACSDK_ERROR(LX(__func__).d("reason", "resourceIdExists").d("id", resourceId)); + return nullptr; + } + + m_ids.insert({resourceId, PowerResourceInfo(isRefCounted, level)}); + + getAggregatedPowerResourceIdLocked(level); + + auto powerId = std::make_shared(resourceId); + return powerId; +} + +bool AggregatedPowerResourceManager::acquire( + const std::shared_ptr& id, + const std::chrono::milliseconds autoReleaseTimeout) { + if (!id) { + ACSDK_ERROR(LX(__func__).d("reason", "nullId")); + return false; + } + + ACSDK_DEBUG9(LX(__func__).d("id", id->getResourceId())); + std::lock_guard lock(m_mutex); + + auto callerIt = m_ids.find(id->getResourceId()); + if (m_ids.end() == callerIt) { + ACSDK_ERROR(LX(__func__).d("reason", "nonExistentId").d("id", id->getResourceId())); + return false; + } + + auto& callerPowerResourceInfo = callerIt->second; + std::shared_ptr aggregatedPowerResourceId = + getAggregatedPowerResourceIdLocked(callerPowerResourceInfo.level); + + // Do not handle any auto release timer cases. + if (autoReleaseTimeout != std::chrono::milliseconds::zero()) { + return m_appPowerResourceManager->acquire(aggregatedPowerResourceId, autoReleaseTimeout); + } + + // Do not dedupe acquire calls if refcount enabled. Let the application PowerResourceManagerInterface + // handle that if desired. + if (callerPowerResourceInfo.isRefCounted || + (!callerPowerResourceInfo.isRefCounted && callerPowerResourceInfo.refCount == 0)) { + callerPowerResourceInfo.refCount++; + m_appPowerResourceManager->acquire(aggregatedPowerResourceId); + } + + return true; +} + +bool AggregatedPowerResourceManager::release(const std::shared_ptr& id) { + if (!id) { + ACSDK_ERROR(LX(__func__).d("reason", "nullId")); + return false; + } + + std::lock_guard lock(m_mutex); + ACSDK_DEBUG9(LX(__func__).d("id", id->getResourceId())); + + auto callerIt = m_ids.find(id->getResourceId()); + if (m_ids.end() == callerIt) { + ACSDK_ERROR(LX(__func__).d("reason", "nonExistentId").d("id", id->getResourceId())); + return false; + } + + auto& callerPowerResourceInfo = callerIt->second; + std::shared_ptr aggregatedPowerResourceId = + getAggregatedPowerResourceIdLocked(callerPowerResourceInfo.level); + + if (callerPowerResourceInfo.refCount > 0) { + callerPowerResourceInfo.refCount--; + m_appPowerResourceManager->release(aggregatedPowerResourceId); + } + + return true; +} + +bool AggregatedPowerResourceManager::close(const std::shared_ptr& id) { + if (!id) { + ACSDK_ERROR(LX(__func__).d("reason", "nullId")); + return false; + } + + std::lock_guard lock(m_mutex); + const std::string idString = id->getResourceId(); + ACSDK_DEBUG9(LX(__func__).d("id", id->getResourceId())); + + auto callerIt = m_ids.find(idString); + if (m_ids.end() == callerIt) { + ACSDK_ERROR(LX(__func__).d("reason", "nonExistentId").d("id", idString)); + return false; + } + + auto& callerPowerResourceInfo = callerIt->second; + auto level = callerPowerResourceInfo.level; + + std::shared_ptr aggregatedPowerResourceId = getAggregatedPowerResourceIdLocked(level); + + for (unsigned int i = 0; i < callerPowerResourceInfo.refCount; i++) { + m_appPowerResourceManager->release(aggregatedPowerResourceId); + } + + // We do not close the underlying aggregated PowerResource to prevent any latency hit from dynamically + // creating/closing. + m_ids.erase(idString); + + return true; +} + +} // namespace power +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/src/Power/PowerMonitor.cpp b/AVSCommon/Utils/src/Power/PowerMonitor.cpp new file mode 100644 index 0000000000..ab5b958765 --- /dev/null +++ b/AVSCommon/Utils/src/Power/PowerMonitor.cpp @@ -0,0 +1,205 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include +#include "AVSCommon/Utils/Power/PowerMonitor.h" +#include "AVSCommon/Utils/Power/AggregatedPowerResourceManager.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace power { + +/// String to identify log entries originating from this file. +static const std::string TAG("PowerMonitor"); + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param The event string for this @c LogEntry. + */ +#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) + +std::mutex PowerMonitor::m_mutex; +std::shared_ptr PowerMonitor::m_monitor; + +std::shared_ptr PowerMonitor::getInstance() { + ACSDK_DEBUG9(LX(__func__)); + + std::lock_guard lock(m_mutex); + if (!m_monitor) { + PowerMonitor::m_monitor = std::shared_ptr(new PowerMonitor()); + } + + return m_monitor; +} + +void PowerMonitor::activate(std::shared_ptr powerManager) { + ACSDK_DEBUG9(LX(__func__)); + + std::lock_guard lock(m_mutex); + if (isActiveLocked()) { + ACSDK_ERROR(LX(__func__).d("reason", "alreadyActive")); + return; + } + + if (powerManager) { + m_powerManager = AggregatedPowerResourceManager::create(powerManager); + } else { + ACSDK_ERROR(LX(__func__).d("reason", "nullPowerManager")); + } +} + +bool PowerMonitor::isActive() { + std::lock_guard lock(m_mutex); + return isActiveLocked(); +} + +bool PowerMonitor::isActiveLocked() { + bool active = m_powerManager != nullptr; + ACSDK_DEBUG9(LX(__func__).d("isActiveLocked", active)); + return active; +} + +void PowerMonitor::deactivate() { + ACSDK_DEBUG9(LX(__func__)); + + std::lock_guard lock(m_mutex); + m_powerManager.reset(); + m_threadPowerResources.clear(); +} + +std::shared_ptr PowerMonitor::getPowerResourceManager() { + ACSDK_DEBUG5(LX(__func__)); + + std::lock_guard lock(m_mutex); + return m_powerManager; +} + +std::shared_ptr PowerMonitor::createLocalPowerResource( + const std::string& identifier, + sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel level) { + ACSDK_DEBUG9(LX(__func__).d("identifier", identifier)); + + std::lock_guard lock(m_mutex); + if (!isActiveLocked()) { + return nullptr; + } + + return PowerResource::create(identifier, m_powerManager, level); +} + +std::shared_ptr PowerMonitor::getThreadPowerResource() { + ACSDK_DEBUG9(LX(__func__).d("threadId", std::this_thread::get_id())); + + std::lock_guard lock(m_mutex); + + if (!isActiveLocked()) { + return nullptr; + } + + auto threadId = std::this_thread::get_id(); + std::shared_ptr resource; + + auto it = m_threadPowerResources.find(threadId); + if (m_threadPowerResources.end() != it) { + resource = it->second; + } + + return resource; +} + +std::shared_ptr PowerMonitor::assignThreadPowerResource(std::shared_ptr powerResource) { + ACSDK_DEBUG9(LX(__func__).d("threadId", std::this_thread::get_id())); + + auto threadId = std::this_thread::get_id(); + std::shared_ptr ret; + + std::lock_guard lock(m_mutex); + if (!isActiveLocked()) { + return ret; + } else if (!powerResource) { + ACSDK_ERROR(LX(__func__).d("reason", "nullPowerResource")); + return ret; + } + + if (m_threadPowerResources.count(threadId) != 0) { + auto existingResource = m_threadPowerResources.at(threadId); + + ACSDK_ERROR(LX(__func__) + .d("threadId", threadId) + .d("reason", "threadAlreadyHasResource") + .d("resourceId", existingResource->getId())); + } else { + m_threadPowerResources.insert({threadId, powerResource}); + ret = powerResource; + } + + return ret; +} + +std::shared_ptr PowerMonitor::getThreadPowerResourceOrCreate( + const std::string& identifier, + sdkInterfaces::PowerResourceManagerInterface::PowerResourceLevel level) { + ACSDK_DEBUG9(LX(__func__)); + + if (!isActive()) { + return nullptr; + } + + auto powerResource = getThreadPowerResource(); + + // Either the wrong identifier was passed in or the thread did not cleanup its resource. + // Will fail gracefully, but this is not behavior guaranteed by the method. + if (powerResource && powerResource->getId() != identifier) { + ACSDK_ERROR(LX(__func__) + .d("reason", "mismatchedIdentifier") + .d("existingIdentifier", powerResource->getId()) + .d("identifier", identifier) + .d("action", "deletingExistingResource")); + + removeThreadPowerResource(); + powerResource.reset(); + } + + if (!powerResource) { + std::lock_guard lock(m_mutex); + if (!m_powerManager) { + ACSDK_ERROR(LX(__func__).d("reason", "nullPowerManager")); + return nullptr; + } + + ACSDK_DEBUG9(LX(__func__) + .d("reason", "creatingPowerResource") + .d("identifier", identifier) + .d("threadId", std::this_thread::get_id())); + powerResource = PowerResource::create(identifier, m_powerManager, level); + + m_threadPowerResources.insert({std::this_thread::get_id(), powerResource}); + } + + return powerResource; +} + +void PowerMonitor::removeThreadPowerResource() { + ACSDK_DEBUG9(LX(__func__).d("threadId", std::this_thread::get_id())); + std::lock_guard lock(m_mutex); + m_threadPowerResources.erase(std::this_thread::get_id()); +} + +} // namespace power +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/src/Power/PowerResource.cpp b/AVSCommon/Utils/src/Power/PowerResource.cpp new file mode 100644 index 0000000000..9045679098 --- /dev/null +++ b/AVSCommon/Utils/src/Power/PowerResource.cpp @@ -0,0 +1,194 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include +#include "AVSCommon/Utils/Power/PowerResource.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace power { + +using namespace avsCommon::sdkInterfaces; + +/// String to identify log entries originating from this file. +static const std::string TAG("PowerResource"); + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param The event string for this @c LogEntry. + */ +#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) + +std::shared_ptr PowerResource::create( + const std::string& identifier, + std::shared_ptr powerManager, + PowerResourceManagerInterface::PowerResourceLevel level, + bool refCounted) { + if (!powerManager) { + ACSDK_ERROR(LX(__func__).d("error", "nullPowerManager")); + return nullptr; + } + + return std::shared_ptr(new PowerResource(identifier, powerManager, level, refCounted)); +} + +PowerResource::~PowerResource() { + ACSDK_DEBUG9(LX(__func__)); + + std::lock_guard lock(m_mutex); + if (m_isFrozen) { + ACSDK_WARN(LX(__func__).d("reason", "isFrozen")); + } + + if (m_powerManager) { + for (uint64_t i = 0; i < m_refCount; i++) { + m_powerManager->release(m_powerResourceId); + } + } + + m_powerManager->close(m_powerResourceId); +} + +PowerResource::PowerResource( + const std::string& identifier, + std::shared_ptr powerManager, + PowerResourceManagerInterface::PowerResourceLevel level, + bool refCounted) : + m_identifier{identifier}, + m_prefixedIdentifier{std::string(PREFIX) + m_identifier}, + m_isRefCounted{refCounted}, + m_refCount{0}, + m_level{level}, + m_isFrozen{false}, + m_powerManager{powerManager} { + m_powerResourceId = m_powerManager->create(m_prefixedIdentifier, m_isRefCounted, m_level); + if (!m_powerResourceId) { + ACSDK_ERROR(LX(__func__).d("reason", "createFailed").d("method", "PowerResourceManagerInterface::create")); + } +} + +bool PowerResource::isRefCounted() const { + ACSDK_DEBUG9(LX(__func__).d("id", m_identifier)); + + return m_isRefCounted; +} + +std::string PowerResource::getId() const { + ACSDK_DEBUG9(LX(__func__).d("id", m_identifier)); + + return m_identifier; +} + +bool PowerResource::isFrozen() const { + std::lock_guard lock(m_mutex); + + ACSDK_DEBUG9(LX(__func__).d("id", m_identifier).d("isFrozen", m_isFrozen).d("refCount", m_refCount)); + + return m_isFrozen; +} + +PowerResourceManagerInterface::PowerResourceLevel PowerResource::getLevel() const { + ACSDK_DEBUG9(LX(__func__).d("id", m_identifier)); + + std::lock_guard lock(m_mutex); + return m_level; +} + +void PowerResource::acquire() { + std::lock_guard lock(m_mutex); + + if (m_isFrozen) { + ACSDK_ERROR(LX(__func__).d("reason", "frozen")); + return; + } + + if (m_powerManager) { + if (m_isRefCounted) { + m_refCount++; + } else { + m_refCount = 1; + } + + m_powerManager->acquire(m_powerResourceId); + } + + ACSDK_DEBUG9(LX(__func__).d("id", m_identifier).d("refCount", m_refCount)); +} + +void PowerResource::release() { + std::lock_guard lock(m_mutex); + + if (m_isFrozen) { + ACSDK_ERROR(LX(__func__).d("reason", "frozen")); + return; + } + + if (m_powerManager) { + if (m_isRefCounted) { + if (m_refCount > 0) { + m_refCount--; + } + } else { + m_refCount = 0; + } + + m_powerManager->release(m_powerResourceId); + } + + ACSDK_DEBUG9(LX(__func__).d("id", m_identifier).d("refCount", m_refCount)); +} + +void PowerResource::freeze() { + ACSDK_DEBUG9(LX(__func__).d("id", m_identifier).d("refCount", m_refCount)); + + std::lock_guard lock(m_mutex); + if (m_isFrozen) { + ACSDK_DEBUG9(LX(__func__).d("reason", "alreadyFrozen")); + return; + } + + if (m_powerManager) { + m_isFrozen = true; + + for (uint64_t i = 0; i < m_refCount; i++) { + m_powerManager->release(m_powerResourceId); + } + } +} + +void PowerResource::thaw() { + ACSDK_DEBUG9(LX(__func__).d("id", m_identifier).d("refCount", m_refCount)); + + std::lock_guard lock(m_mutex); + if (!m_isFrozen) { + ACSDK_DEBUG9(LX(__func__).d("reason", "notFrozen")); + return; + } + + if (m_powerManager) { + for (uint64_t i = 0; i < m_refCount; i++) { + m_powerManager->acquire(m_powerResourceId); + } + } + + m_isFrozen = false; +} + +} // namespace power +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/src/Power/WakeGuard.cpp b/AVSCommon/Utils/src/Power/WakeGuard.cpp new file mode 100644 index 0000000000..a3d5aa268c --- /dev/null +++ b/AVSCommon/Utils/src/Power/WakeGuard.cpp @@ -0,0 +1,38 @@ +/* + * 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/Power/WakeGuard.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace power { + +WakeGuard::WakeGuard(std::shared_ptr powerResource) : m_powerResource{powerResource} { + if (m_powerResource) { + m_powerResource->acquire(); + } +} + +WakeGuard::~WakeGuard() { + if (m_powerResource) { + m_powerResource->release(); + } +} + +} // namespace power +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/src/SystemClockMonitor.cpp b/AVSCommon/Utils/src/SystemClockMonitor.cpp index 9f3cf763ed..b326918ce0 100644 --- a/AVSCommon/Utils/src/SystemClockMonitor.cpp +++ b/AVSCommon/Utils/src/SystemClockMonitor.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/AVSCommon/Utils/src/Threading/ConditionVariableWrapper.cpp b/AVSCommon/Utils/src/Threading/ConditionVariableWrapper.cpp new file mode 100644 index 0000000000..c78c7fb59a --- /dev/null +++ b/AVSCommon/Utils/src/Threading/ConditionVariableWrapper.cpp @@ -0,0 +1,206 @@ +/* + * 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/Utils/Threading/ConditionVariableWrapper.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace threading { + +using namespace avsCommon::sdkInterfaces; + +/// String to identify log entries originating from this file. +static const std::string TAG(ConditionVariableWrapper::getTag()); + +/** + * Create a LogEntry using this file's TAG and the specified event string. + * + * @param The event string for this @c LogEntry. + */ +#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) + +std::atomic ConditionVariableWrapper::g_id{0}; + +ConditionVariableWrapper::ConditionVariableWrapper() : m_id{g_id++} { + ACSDK_DEBUG9(LX(__func__).d("id", m_id)); + m_notifyOnePowerResourceRefCount = 0; + +#ifdef ENABLE_LPM + std::stringstream ss; + ss << TAG << "_" << m_id; + + m_notifyOnePowerResource = power::PowerMonitor::getInstance()->createLocalPowerResource(ss.str()); +#endif +} + +void ConditionVariableWrapper::notifyOne() { + ACSDK_DEBUG5(LX(__func__)); + +#ifdef ENABLE_LPM + std::lock_guard lock(m_mutex); + // Only acquire if we have a guaranteed thread to awake and release the resource. + if (m_notifyOnePowerResource && m_notifyOnePowerResourceRefCount < m_frozenResources.size()) { + m_notifyOnePowerResource->acquire(); + m_notifyOnePowerResourceRefCount++; + } +#endif + + m_cv.notify_one(); +}; + +void ConditionVariableWrapper::notifyAll() { + ACSDK_DEBUG5(LX(__func__)); + +#ifdef ENABLE_LPM + /* + * If no threads are blocked, we won't thaw any resources. + * Therefore we won't thaw a resource whose associated thread is stays blocked. + */ + std::lock_guard lock(m_mutex); + for (auto powerResource : m_frozenResources) { + ACSDK_DEBUG9(LX(__func__).d("reason", "thawingResource").d("id", powerResource->getId())); + powerResource->thaw(); + } + m_frozenResources.clear(); +#endif + + m_cv.notify_all(); +}; + +void ConditionVariableWrapper::waitInner(std::unique_lock& lock, std::function pred) { + ACSDK_DEBUG5(LX(__func__)); +#ifdef ENABLE_LPM + auto threadPowerResource = power::PowerMonitor::getInstance()->getThreadPowerResource(); + + // PowerMonitor is not active or no associated thread PowerResource, do a standard wait. + if (!threadPowerResource) { + ACSDK_DEBUG9(LX(__func__).d("reason", "powerManagementNotNeeded").d("action", "rawWait")); + m_cv.wait(lock, pred); + return; + } + + auto predicateWithPowerLogic = [this, pred, threadPowerResource]() { + // Could be due to spurious wakeup or notifyOne(). + if (threadPowerResource->isFrozen()) { + ACSDK_DEBUG9(LX("waitPredicate") + .d("reason", "spuriousOrNotifyOne") + .d("thawingResource", threadPowerResource->getId()) + .d("threadId", std::this_thread::get_id())); + threadPowerResource->thaw(); + + std::lock_guard innerLock(m_mutex); + m_frozenResources.erase(threadPowerResource); + } + + { + std::lock_guard innerLock(m_mutex); + + // In case we were unblocked due to notifyOne(). + if (m_notifyOnePowerResource && m_notifyOnePowerResourceRefCount > 0) { + m_notifyOnePowerResource->release(); + m_notifyOnePowerResourceRefCount--; + } + } + + bool exit = pred(); + if (!exit) { + { + ACSDK_DEBUG9(LX("waitPredicate") + .d("reason", "notExiting") + .d("freezingResource", threadPowerResource->getId()) + .d("threadId", std::this_thread::get_id())); + + std::lock_guard innerLock(m_mutex); + m_frozenResources.insert(threadPowerResource); + } + + /* + * It is acceptable if system transitions to low power mode after freeze(). + * The notifying thread will need to take the system out of low power mode. + */ + threadPowerResource->freeze(); + } + + return exit; + }; + + { + std::lock_guard lock(m_mutex); + m_frozenResources.insert(threadPowerResource); + } + + threadPowerResource->freeze(); + /* + * It is acceptable if system transitions to low power mode before the wait(). + * The notifying thread will need to take the system out of low power mode. + * If pred is satisfied, then wait() will exit immediately. Otherwise, we will continue + * waiting as per usual. + */ + m_cv.wait(lock, predicateWithPowerLogic); +#else + m_cv.wait(lock, pred); +#endif +} + +bool ConditionVariableWrapper::waitForInner( + std::unique_lock& lock, + std::chrono::nanoseconds relTime, + std::function pred) { + ACSDK_DEBUG9(LX(__func__).d("duration", relTime.count())); + +#ifdef ENABLE_LPM + if (!power::PowerMonitor::getInstance()->getThreadPowerResource()) { + return m_cv.wait_for(lock, relTime, pred); + } + + bool timedOut = false; + timing::Timer timer; + std::mutex& outerMutex = *lock.mutex(); + + timer.start(relTime, [this, &timedOut, &outerMutex]() { + ACSDK_DEBUG9(LX("timer").d("reason", "timedOut")); + + 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; }; + + wait(lock, predicateOrTimedout); + + // If timer is still running, stop. + if (timer.isActive()) { + ACSDK_DEBUG9(LX("timer").d("reason", "stopBeforeExpiration")); + timer.stop(); + } + + return pred(); +#else + return m_cv.wait_for(lock, relTime, pred); +#endif +} + +} // namespace threading +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/src/Timer.cpp b/AVSCommon/Utils/src/Timer.cpp index c32d85ce7f..811e3b47d9 100644 --- a/AVSCommon/Utils/src/Timer.cpp +++ b/AVSCommon/Utils/src/Timer.cpp @@ -13,14 +13,39 @@ * permissions and limitations under the License. */ +#include + #include "AVSCommon/Utils/Timing/Timer.h" +#include "AVSCommon/Utils/Timing/TimerDelegateFactory.h" namespace alexaClientSDK { namespace avsCommon { namespace utils { namespace timing { -Timer::Timer() : m_running(false), m_stopping(false) { +/// String to identify log entries originating from this file. +static const std::string TAG(Timer::getTag()); + +/** + * 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) + +Timer::Timer(std::shared_ptr timerDelegateFactory) { + if (!timerDelegateFactory) { + timerDelegateFactory = std::make_shared(); + if (!timerDelegateFactory) { + ACSDK_ERROR(LX(__func__).d("reason", "nullTimerDelegateFactory")); + return; + } + } + + m_timer = timerDelegateFactory->getTimerDelegate(); + if (!m_timer) { + ACSDK_ERROR(LX(__func__).d("reason", "nullTimerDelegate")); + } } Timer::~Timer() { @@ -28,26 +53,17 @@ Timer::~Timer() { } void Timer::stop() { - { - std::lock_guard waitLock(m_waitMutex); - if (m_running) { - m_stopping = true; - } - m_waitCondition.notify_all(); - } - - std::lock_guard joinLock(m_joinMutex); - if (std::this_thread::get_id() != m_thread.get_id() && m_thread.joinable()) { - m_thread.join(); + if (m_timer) { + m_timer->stop(); } } bool Timer::isActive() const { - return m_running; + return m_timer && m_timer->isActive(); } bool Timer::activate() { - return !m_running.exchange(true); + return m_timer && m_timer->activate(); } } // namespace timing diff --git a/AVSCommon/Utils/src/Timing/TimerDelegate.cpp b/AVSCommon/Utils/src/Timing/TimerDelegate.cpp new file mode 100644 index 0000000000..1ab333cca6 --- /dev/null +++ b/AVSCommon/Utils/src/Timing/TimerDelegate.cpp @@ -0,0 +1,142 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include +#include "AVSCommon/Utils/Timing/TimerDelegate.h" +#include + +/// String to identify log entries originating from this file. +static const std::string TAG("TimerDelegate"); + +/** + * 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 { + +TimerDelegate::TimerDelegate() : m_running{false}, m_stopping{false} { +} + +void TimerDelegate::start( + std::chrono::nanoseconds delay, + std::chrono::nanoseconds period, + PeriodType periodType, + size_t maxCount, + std::function task) { + std::lock_guard lock(m_callMutex); + cleanupLocked(); + activateLocked(); + m_thread = std::thread(&TimerDelegate::timerLoop, this, delay, period, periodType, maxCount, task); +} + +void TimerDelegate::timerLoop( + std::chrono::nanoseconds delay, + std::chrono::nanoseconds period, + PeriodType periodType, + size_t maxCount, + std::function task) { + // Use FinallyGuard to ensure that m_stopping and m_running get reset on completion or exception + avsCommon::utils::error::FinallyGuard guard{[this] { + m_stopping = false; + m_running = false; + return; + }}; + + // Timepoint to measure delay/period against. + auto now = std::chrono::steady_clock::now(); + + // Flag indicating whether we've drifted off schedule. + bool offSchedule = false; + + for (size_t count = 0; maxCount == FOREVER || count < maxCount; ++count) { + auto waitTime = (0 == count) ? delay : period; + { + std::unique_lock lock(m_waitMutex); + + // Wait for stop() or a delay/period to elapse. + if (m_waitCondition.wait_until(lock, now + waitTime, [this]() { return m_stopping; })) { + return; + } + } + + switch (periodType) { + case PeriodType::ABSOLUTE: + // Update our estimate of where we should be after the delay. + now += waitTime; + + // Run the task if we're still on schedule. + if (!offSchedule) { + task(); + } + + // If the task runtime put us off schedule, skip the next task run. + if (now + period < std::chrono::steady_clock::now()) { + offSchedule = true; + } else { + offSchedule = false; + } + break; + + case PeriodType::RELATIVE: + task(); + now = std::chrono::steady_clock::now(); + break; + } + } +} + +void TimerDelegate::stop() { + std::lock_guard lock(m_callMutex); + + if (m_running) { + std::lock_guard lock(m_waitMutex); + m_stopping = true; + } + + m_waitCondition.notify_all(); + cleanupLocked(); +} + +bool TimerDelegate::activate() { + std::lock_guard lock(m_callMutex); + return activateLocked(); +} + +bool TimerDelegate::activateLocked() { + auto previous = m_running; + m_running = true; + return !previous; +} + +bool TimerDelegate::isActive() const { + return m_running; +} + +void TimerDelegate::cleanupLocked() { + if (std::this_thread::get_id() != m_thread.get_id() && m_thread.joinable()) { + m_thread.join(); + } +} + +} // namespace timing +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/src/Timing/TimerDelegateFactory.cpp b/AVSCommon/Utils/src/Timing/TimerDelegateFactory.cpp new file mode 100644 index 0000000000..02ae9ae90a --- /dev/null +++ b/AVSCommon/Utils/src/Timing/TimerDelegateFactory.cpp @@ -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. + */ + +#include +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace timing { + +bool TimerDelegateFactory::supportsLowPowerMode() { + return false; +} + +std::unique_ptr TimerDelegateFactory::getTimerDelegate() { + return memory::make_unique(); +} + +} // namespace timing +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/src/UUIDGeneration.cpp b/AVSCommon/Utils/src/UUIDGeneration.cpp index 90cbfc7bf7..8984ff1728 100644 --- a/AVSCommon/Utils/src/UUIDGeneration.cpp +++ b/AVSCommon/Utils/src/UUIDGeneration.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "AVSCommon/Utils/Logger/Logger.h" #include "AVSCommon/Utils/UUIDGeneration/UUIDGeneration.h" @@ -63,18 +64,40 @@ static bool g_seedNeeded = true; /// Lock to avoid collisions in generationing uuid and setting seed. static std::mutex g_mutex; -/// Unique g_salt to use, settable by caller. Must not be accessed unless g_mutex is locked. -static std::string g_salt("default"); - /// Entropy Threshold for sufficient uniqueness. Value chosen by experiment. static const double ENTROPY_THRESHOLD = 600; +/// extra seeds +static const size_t MAX_SEEDS_POOL_SIZE = 1024; + +/// pool of seeds +static std::list seedsPool; + +/// last time generateUUID was invoked +static uint64_t lastInvokeTime; + /// Catch for platforms where entropy is a hard coded value. Value chosen by experiment. static const int ENTROPY_REPEAT_THRESHOLD = 16; +/// The default read entropy function. This can be customized using UUIDGeneration::setEntropyReader(). +static std::function readEntropyFunc = []() { + std::random_device rd; + return rd.entropy(); +}; + +void setEntropyReader(std::function func) { + readEntropyFunc = func; +} + void setSalt(const std::string& newSalt) { std::unique_lock lock(g_mutex); - g_salt = newSalt; + std::copy_n(newSalt.begin(), std::min(newSalt.size(), MAX_SEEDS_POOL_SIZE), std::front_inserter(seedsPool)); + g_seedNeeded = true; +} + +void addSeeds(const std::vector& seeds) { + std::unique_lock lock(g_mutex); + std::copy_n(seeds.begin(), std::min(seeds.size(), MAX_SEEDS_POOL_SIZE), std::front_inserter(seedsPool)); g_seedNeeded = true; } @@ -91,10 +114,10 @@ void setSalt(const std::string& newSalt) { * @return A hex string of length @c numDigits. */ static const std::string generateHexWithReplacement( - std::independent_bits_engine& ibe, + std::independent_bits_engine& ibe, unsigned int numDigits, uint8_t replacementBits, - uint16_t numReplacementBits) { + uint8_t numReplacementBits) { if (numReplacementBits > MAX_NUM_REPLACEMENT_BITS) { ACSDK_ERROR(LX("generateHexWithReplacementFailed") .d("reason", "replacingMoreBitsThanProvided") @@ -113,15 +136,9 @@ static const std::string generateHexWithReplacement( const size_t arrayLen = (numDigits + 1) / 2; - std::vector shorts(arrayLen); - std::generate(shorts.begin(), shorts.end(), std::ref(ibe)); - - // Makes assumption that 1 digit = 4 bits. std::vector bytes(arrayLen); + std::generate(bytes.begin(), bytes.end(), std::ref(ibe)); - for (size_t i = 0; i < arrayLen; i++) { - bytes[i] = static_cast(shorts[i]); - } // Replace the specified number of bits from the first byte. bytes.at(0) &= (0xff >> numReplacementBits); replacementBits &= (0xff << (MAX_NUM_REPLACEMENT_BITS - numReplacementBits)); @@ -148,22 +165,44 @@ static const std::string generateHexWithReplacement( * @return A hex string of length @c numDigits. */ static const std::string generateHex( - std::independent_bits_engine& ibe, + std::independent_bits_engine& ibe, unsigned int numDigits) { return generateHexWithReplacement(ibe, numDigits, 0, 0); } +/** + * Add default seed to the pool, should only be used in generateUUID and locked by g_mutex. + */ +static void addDefaultSeedLocked() { + std::random_device rd; + uint64_t timeSeed = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + if (lastInvokeTime > 0) { + seedsPool.push_front((uint32_t)(timeSeed - lastInvokeTime)); // interval between two invocations + } + lastInvokeTime = timeSeed; + seedsPool.push_front((uint32_t)timeSeed); // lower 32bits of current time + seedsPool.push_front(rd()); // random device + seedsPool.push_front( + reinterpret_cast(&timeSeed)); // lower 32bits of memory address of temporary variable + + if (seedsPool.size() > MAX_SEEDS_POOL_SIZE) { + seedsPool.resize(MAX_SEEDS_POOL_SIZE); + } +} + const std::string generateUUID() { - static std::independent_bits_engine ibe; + static std::independent_bits_engine ibe; std::unique_lock lock(g_mutex); - static int consistentEntropyReports = 0; static double priorEntropyResult = 0; if (g_seedNeeded) { - std::random_device rd; + addDefaultSeedLocked(); + std::seed_seq seed(seedsPool.begin(), seedsPool.end()); + ibe.seed(seed); + + double currentEntropy = readEntropyFunc(); - double currentEntropy = rd.entropy(); if (std::fabs(currentEntropy - priorEntropyResult) < std::numeric_limits::epsilon()) { ++consistentEntropyReports; } else { @@ -171,24 +210,16 @@ const std::string generateUUID() { } priorEntropyResult = currentEntropy; - long randomTimeComponent = rd() + std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()) - .count(); - std::string fullSeed = g_salt + std::to_string(randomTimeComponent); - std::seed_seq seed(fullSeed.begin(), fullSeed.end()); - ibe.seed(seed); - if (currentEntropy > ENTROPY_THRESHOLD) { g_seedNeeded = false; } else { ACSDK_WARN(LX("low entropy on call to generate UUID").d("current entropy", currentEntropy)); - } - - if (consistentEntropyReports > ENTROPY_REPEAT_THRESHOLD) { - g_seedNeeded = false; - ACSDK_WARN(LX("multiple repeat values for entropy") - .d("current entropy", currentEntropy) - .d("consistent entropy reports", consistentEntropyReports)); + if (consistentEntropyReports > ENTROPY_REPEAT_THRESHOLD) { + g_seedNeeded = false; + ACSDK_WARN(LX("multiple repeat values for entropy") + .d("current entropy", currentEntropy) + .d("consistent entropy reports", consistentEntropyReports)); + } } } diff --git a/AVSCommon/Utils/src/WaitEvent.cpp b/AVSCommon/Utils/src/WaitEvent.cpp index b607334e8b..d80288ca55 100644 --- a/AVSCommon/Utils/src/WaitEvent.cpp +++ b/AVSCommon/Utils/src/WaitEvent.cpp @@ -25,12 +25,12 @@ WaitEvent::WaitEvent() : m_wakeUpTriggered{false} { void WaitEvent::wakeUp() { std::lock_guard lock{m_mutex}; m_wakeUpTriggered = true; - m_condition.notify_all(); + m_condition.notifyAll(); } bool WaitEvent::wait(const std::chrono::milliseconds& timeout) { std::unique_lock lock{m_mutex}; - return m_condition.wait_for(lock, timeout, [this] { return m_wakeUpTriggered; }); + return m_condition.waitFor(lock, timeout, [this] { return m_wakeUpTriggered; }); } void WaitEvent::reset() { diff --git a/AVSCommon/Utils/src/WavUtils.cpp b/AVSCommon/Utils/src/WavUtils.cpp new file mode 100644 index 0000000000..bbe5086075 --- /dev/null +++ b/AVSCommon/Utils/src/WavUtils.cpp @@ -0,0 +1,294 @@ +/* + * 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/Utils/Endian.h" +#include "AVSCommon/Utils/Logger/Logger.h" +#include "AVSCommon/Utils/WavUtils.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { + +static const std::string TAG("WavUtils"); + +#define LX(event) alexaClientSDK::avsCommon::utils::logger::LogEntry(TAG, event) + +static const int SHORT_CHUNK_SIZE = 2; + +static const int LONG_CHUNK_SIZE = 4; + +/** + * Helper Method to write uint16_t into WAVE byte vector. + * + * @param input The input being written into the header. + * @param [out] header The WAVE header to write to. + */ +static void writeToWavHeader(uint16_t input, ByteVector* header) { + if (!header) { + ACSDK_ERROR(LX(__func__).d("reason", "nullHeader")); + return; + } + + // WAVE Header should be written with little endian value + if (!littleEndianMachine()) { + input = swapEndian(input); + } + + header->insert( + header->end(), + reinterpret_cast(&input), + reinterpret_cast(&input) + sizeof(input)); +}; + +/** + * Helper Method to write uint32_t into WAVE byte vector. + * + * @param input The input being written into the header. + * @param [out] header The WAVE header to write to. + */ +static void writeToWavHeader(uint32_t input, ByteVector* header) { + if (!header) { + ACSDK_ERROR(LX(__func__).d("reason", "nullHeader")); + return; + } + + // WAVE Header should be written with little endian value + if (!littleEndianMachine()) { + input = swapEndian(input); + } + + header->insert( + header->end(), + reinterpret_cast(&input), + reinterpret_cast(&input) + sizeof(input)); +}; + +/** + * Helper Method to write 4 char into WAVE byte vector. + * + * @param input The input being written into the header. + * @param [out] header The WAVE header to write to. + */ +static void writeToWavHeader(const char* input, ByteVector* header) { + if (!header) { + ACSDK_ERROR(LX(__func__).d("reason", "nullHeader")); + return; + } + + if (!input) { + ACSDK_ERROR(LX(__func__).d("reason", "nullInput")); + return; + } + + auto inputSize = strlen(input); + if (inputSize != LONG_CHUNK_SIZE) { + ACSDK_ERROR(LX(__func__).d("reason", "inputInvalidSize").d("inputSize", inputSize)); + return; + } + + header->insert( + header->end(), + reinterpret_cast(input), + reinterpret_cast(input) + LONG_CHUNK_SIZE); +}; + +/** + * Helper Method to read 2 bytes from WAVE header. + * + * @param buffer The buffer containing the WAVE header. + * @param offset The offset to read from the buffer. + * @return The 2 bytes as a uint_16t. + */ +static uint16_t readShortFromHeader(char* buffer, unsigned int offset) { + if (!buffer) { + ACSDK_ERROR(LX(__func__).d("reason", "nullBuffer")); + } + uint16_t output; + memcpy(&output, buffer + offset, SHORT_CHUNK_SIZE); + if (!littleEndianMachine()) { + output = swapEndian(output); + } + return output; +} + +/** + * Helper Method to read 4 bytes from WAVE header. + * + * @param buffer The buffer containing the WAVE header. + * @param offset The offset to read from the buffer. + * @return The 4 bytes as a uint_32t. + */ +static uint32_t readLongFromHeader(char* buffer, unsigned int offset) { + if (!buffer) { + ACSDK_ERROR(LX(__func__).d("reason", "nullBuffer")); + } + uint32_t output; + memcpy(&output, buffer + offset, LONG_CHUNK_SIZE); + if (!littleEndianMachine()) { + output = swapEndian(output); + } + return output; +} + +/** + * Helper Method to read 4 char from WAVE header. + * + * @param buffer The buffer containing the WAVE header. + * @param offset The offset to read from the buffer. + * @return The 4 char as a uint_32t. + */ +static uint32_t readFourCharFromHeader(char* buffer, unsigned int offset) { + if (!buffer) { + ACSDK_ERROR(LX(__func__).d("reason", "nullBuffer")); + } + uint32_t output; + memcpy(&output, buffer + offset, LONG_CHUNK_SIZE); + return output; +} + +ByteVector generateWavHeader( + unsigned int bytesPerSample, + unsigned int channels, + unsigned int rate, + std::chrono::milliseconds totalDuration, + bool isPCM) { + uint32_t numberSample = static_cast(totalDuration.count()) * (rate / 100); + if (numberSample > 0) { + numberSample /= 10; + } + int headerSize = isPCM ? PCM_HEADER_SIZE : NON_PCM_HEADER_SIZE; + + ByteVector output; + + // riffId + writeToWavHeader(ID_RIFF, &output); + // riffSz + writeToWavHeader(static_cast(numberSample * channels * bytesPerSample + headerSize - 8), &output); + // riffFmt + writeToWavHeader(ID_WAVE, &output); + // fmtId + writeToWavHeader(ID_FMT, &output); + // fmtSz + writeToWavHeader(FLOAT_SUBCHUNK_SZ, &output); + // audioFormat + writeToWavHeader(FORMAT_IEEE_FLOAT, &output); + // numChannels + writeToWavHeader(static_cast(channels), &output); + // sampleRate + writeToWavHeader(static_cast(rate), &output); + // byteRate + writeToWavHeader(static_cast((bytesPerSample * channels * rate)), &output); + // blockAlign + writeToWavHeader(static_cast(bytesPerSample * channels), &output); + // bitsPerSample + writeToWavHeader(static_cast(bytesPerSample * BITS_PER_BYTE), &output); + if (!isPCM) { + // cbSz + writeToWavHeader(static_cast(0), &output); + // factId + writeToWavHeader(ID_FACT, &output); + // factSz + writeToWavHeader(FACT_CHUNK_SZ, &output); + // factSampleLen + writeToWavHeader(static_cast(channels * numberSample), &output); + } + // dataId + writeToWavHeader(ID_DATA, &output); + // dataSz + writeToWavHeader((uint32_t)(numberSample * channels * bytesPerSample), &output); + + return output; +} + +bool readWAVFile( + const std::string& absoluteFilePath, + std::vector* audioBuffer, + WavHeader& wavHeader, + bool isPCM) { + if (!audioBuffer) { + ACSDK_ERROR(LX("readAudioFileFailed").d("reason", "audioBuffer is nullptr")); + return false; + } + // Attempt to open the given file. + std::ifstream inputFile(absoluteFilePath, std::ios::binary); + if (!inputFile.good()) { + ACSDK_ERROR(LX("readAudioFileFailed").d("reason", "unable to open file")); + return false; + } + + // Check if the file size is at least the size of a .wav file header. + inputFile.seekg(0, std::ios::end); + int fileLengthInBytes = inputFile.tellg(); + + const int headerSize = isPCM ? PCM_HEADER_SIZE : NON_PCM_HEADER_SIZE; + if (fileLengthInBytes <= headerSize) { + ACSDK_ERROR(LX("readAudioFileFailed").d("reason", "file size less than RIFF header")); + return false; + } + + inputFile.seekg(0, std::ios::beg); + + // Read each part of the WAVE header into audioBuffer + std::vector buffer(headerSize); + inputFile.read(buffer.data(), headerSize); + + char* pBuffer = buffer.data(); + + if (static_cast(inputFile.gcount()) != static_cast(headerSize)) { + ACSDK_ERROR(LX("readAudioFileFailed").d("reason", "failed reading header")); + return false; + } + + wavHeader.riffId = readFourCharFromHeader(pBuffer, RIFF_ID_OFFSET); + wavHeader.riffSz = readLongFromHeader(pBuffer, RIFF_SZ_OFFSET); + wavHeader.riffFmt = readFourCharFromHeader(pBuffer, RIFF_FMT_OFFSET); + wavHeader.fmtId = readFourCharFromHeader(pBuffer, FMT_ID_OFFSET); + wavHeader.fmtSz = readLongFromHeader(pBuffer, FMT_SZ_OFFSET); + wavHeader.audioFormat = readShortFromHeader(pBuffer, AUDIO_FORMAT_OFFSET); + wavHeader.numChannels = readShortFromHeader(pBuffer, NUM_CHANNELS_OFFSET); + wavHeader.sampleRate = readLongFromHeader(pBuffer, SAMPLE_RATE_OFFSET); + wavHeader.byteRate = readLongFromHeader(pBuffer, BYTE_RATE_OFFSET); + wavHeader.blockAlign = readShortFromHeader(pBuffer, BLOCK_ALIGN_OFFSET); + wavHeader.bitsPerSample = readShortFromHeader(pBuffer, BITS_PER_SAMPLE_OFFSET); + if (!isPCM) { + wavHeader.cbSz = readShortFromHeader(pBuffer, CB_SZ_OFFSET); + wavHeader.factId = readFourCharFromHeader(pBuffer, FACT_ID_OFFSET); + wavHeader.factSz = readLongFromHeader(pBuffer, FACT_SZ_OFFSET); + wavHeader.factSampleLen = readLongFromHeader(pBuffer, FACT_SAMPLE_LEN_OFFSET); + } + wavHeader.dataId = readLongFromHeader(pBuffer, DATA_ID_OFFSET + (isPCM ? 0 : NON_PCM_OFFSET)); + wavHeader.dataSz = readLongFromHeader(pBuffer, DATA_SZ_OFFSET + (isPCM ? 0 : NON_PCM_OFFSET)); + + // Read the remainder of the wav file (excluding header) into the audioBuffer. + int numSamples = (fileLengthInBytes - headerSize) / sizeof(uint16_t); + + audioBuffer->resize(numSamples, 0); + inputFile.read(reinterpret_cast(&(audioBuffer->at(0))), numSamples * sizeof(uint16_t)); + + // Check that all audio data was read successfully. + if (static_cast(inputFile.gcount()) != (numSamples * sizeof(uint16_t))) { + ACSDK_ERROR(LX("readAudioFileFailed").d("reason", "failed reading audio data")); + return false; + } + + return true; +}; + +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/test/AVSCommon/Utils/MediaPlayer/PlaybackContextTest.cpp b/AVSCommon/Utils/test/AVSCommon/Utils/MediaPlayer/PlaybackContextTest.cpp index 8423635260..9e9c3f9040 100644 --- a/AVSCommon/Utils/test/AVSCommon/Utils/MediaPlayer/PlaybackContextTest.cpp +++ b/AVSCommon/Utils/test/AVSCommon/Utils/MediaPlayer/PlaybackContextTest.cpp @@ -26,6 +26,8 @@ namespace mediaPlayer { namespace test { const static std::string AUTHORIZATION = "Authorization"; +const static std::string COOKIE = "Cookie"; + TEST(PlaybackContextTest, test_validateHeadersWithEmptyAndValidData) { // Test with empty headers PlaybackContext playbackContext; @@ -48,6 +50,29 @@ TEST(PlaybackContextTest, test_validateMixValidInvalidHeaders) { EXPECT_EQ(playbackContext.audioSegmentConfig.size(), 0u); EXPECT_EQ(playbackContext.allConfig.size(), 0u); } + // Test with one valid header Cookie + { + PlaybackContext playbackContext; + playbackContext.keyConfig[COOKIE] = "abcd"; + + EXPECT_TRUE(validatePlaybackContextHeaders(&playbackContext)); + EXPECT_EQ(playbackContext.keyConfig.size(), 1u); + EXPECT_EQ(playbackContext.manifestConfig.size(), 0u); + EXPECT_EQ(playbackContext.audioSegmentConfig.size(), 0u); + EXPECT_EQ(playbackContext.allConfig.size(), 0u); + } + // Test with one valid header Cookie and one valid header Authorization + { + PlaybackContext playbackContext; + playbackContext.keyConfig[AUTHORIZATION] = "abcd"; + playbackContext.keyConfig[COOKIE] = "abcd"; + + EXPECT_TRUE(validatePlaybackContextHeaders(&playbackContext)); + EXPECT_EQ(playbackContext.keyConfig.size(), 2u); + EXPECT_EQ(playbackContext.manifestConfig.size(), 0u); + EXPECT_EQ(playbackContext.audioSegmentConfig.size(), 0u); + EXPECT_EQ(playbackContext.allConfig.size(), 0u); + } // Test with one valid and invalid header { PlaybackContext playbackContext; @@ -71,13 +96,25 @@ TEST(PlaybackContextTest, test_validateMixValidInvalidHeaders) { } TEST(PlaybackContextTest, test_validateHeadersWithMaxValueSize) { - // Test with value size more than 4096 characters - PlaybackContext playbackContext; - playbackContext.allConfig[AUTHORIZATION] = - R"(abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ)"; + // Test with Authorization header value size more than 4096 characters + { + PlaybackContext playbackContext; + playbackContext.allConfig[AUTHORIZATION] = + R"(abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ)"; - EXPECT_FALSE(validatePlaybackContextHeaders(&playbackContext)); - EXPECT_EQ(playbackContext.allConfig.size(), 0u); + EXPECT_FALSE(validatePlaybackContextHeaders(&playbackContext)); + EXPECT_EQ(playbackContext.allConfig.size(), 0u); + } + // Test with Cookie header value size more than 4096 characters and valid Authorization header size + { + PlaybackContext playbackContext; + playbackContext.allConfig[AUTHORIZATION] = "abcd"; + playbackContext.allConfig[COOKIE] = + R"(abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ)"; + + EXPECT_FALSE(validatePlaybackContextHeaders(&playbackContext)); + EXPECT_EQ(playbackContext.allConfig.size(), 1u); + } } TEST(PlaybackContextTest, test_headersWithMaxKeySize) { @@ -93,6 +130,18 @@ TEST(PlaybackContextTest, test_headersWithMaxKeySize) { EXPECT_EQ(playbackContext.keyConfig.size(), 0u); } +TEST(PlaybackContextTest, test_headersWithMaliciousCharacters) { + PlaybackContext playbackContext; + playbackContext.keyConfig["x-\n"] = "trktrk\n"; + playbackContext.manifestConfig["x-\r"] = "trktrk\r"; + playbackContext.audioSegmentConfig[""] = ""; + + EXPECT_FALSE(validatePlaybackContextHeaders(&playbackContext)); + EXPECT_EQ(playbackContext.keyConfig.size(), 0u); + EXPECT_EQ(playbackContext.manifestConfig.size(), 0u); + EXPECT_EQ(playbackContext.audioSegmentConfig.size(), 0u); +} + } // namespace test } // namespace mediaPlayer } // namespace utils diff --git a/AVSCommon/Utils/test/AVSCommon/Utils/Power/AggregatedPowerResourceManagerTest.cpp b/AVSCommon/Utils/test/AVSCommon/Utils/Power/AggregatedPowerResourceManagerTest.cpp new file mode 100644 index 0000000000..1845154d4e --- /dev/null +++ b/AVSCommon/Utils/test/AVSCommon/Utils/Power/AggregatedPowerResourceManagerTest.cpp @@ -0,0 +1,328 @@ + +/* + * 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 + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace power { +namespace test { + +using namespace ::testing; +using namespace avsCommon::sdkInterfaces; +using namespace avsCommon::sdkInterfaces::test; + +// Component name. +static std::string TEST_ID = "test"; + +// Component level. +static PowerResourceManagerInterface::PowerResourceLevel TEST_LEVEL = + PowerResourceManagerInterface::PowerResourceLevel::STANDBY_LOW; + +// Component refCount configuration. +static bool TEST_REFCOUNTED = true; + +MATCHER_P( + HasPowerResourceId, + /* std::string */ id, + "") { + std::shared_ptr powerResourceId = arg; + if (!powerResourceId) { + return false; + } + + return id == powerResourceId->getResourceId(); +} + +class AggregatedPowerResourceManagerTest : public ::testing::Test { +public: + // SetUp. + void SetUp() override; + + /// PowerResourceManagerInterface Mock. + std::shared_ptr> m_mockAppPowerManager; + + /// PowerResource under test. + std::shared_ptr m_powerManager; +}; + +void AggregatedPowerResourceManagerTest::SetUp() { + m_mockAppPowerManager = std::make_shared>(); + m_mockAppPowerManager->setDefaultBehavior(); + + m_powerManager = AggregatedPowerResourceManager::create(m_mockAppPowerManager); +} + +/** + * Test creating an instance with a nullptr fails. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_factoryMethodWithNullArgument) { + // Some versions of gcc cannot resolve the overload if calling AggregatedPowerResourceManager::create(nullptr) + std::shared_ptr nullPowerManager = nullptr; + EXPECT_THAT(AggregatedPowerResourceManager::create(nullPowerManager), IsNull()); +} + +/** + * Test creating multiple resources with the same ID fails. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_createMultipleSameId) { + EXPECT_THAT(m_powerManager->create(TEST_ID, TEST_REFCOUNTED, TEST_LEVEL), NotNull()); + EXPECT_THAT(m_powerManager->create(TEST_ID, TEST_REFCOUNTED, TEST_LEVEL), IsNull()); +} + +/** + * Test creating multiple resources with the same level + * results in one resource from the app PowerResourceManagerInterface perspective. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_createMultipleSameLevel) { + EXPECT_CALL(*m_mockAppPowerManager, create(_, _, TEST_LEVEL)); + + m_powerManager->create(TEST_ID, TEST_REFCOUNTED, TEST_LEVEL); + m_powerManager->create(TEST_ID + "2", TEST_REFCOUNTED, TEST_LEVEL); + m_powerManager->create(TEST_ID + "3", TEST_REFCOUNTED, TEST_LEVEL); +} + +/** + * Test creating multiple resources with different level + * results in one resource from the app PowerResourceManagerInterface perspective. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_createMultipleDifferentLevels) { + EXPECT_CALL(*m_mockAppPowerManager, create(_, _, TEST_LEVEL)); + EXPECT_CALL(*m_mockAppPowerManager, create(_, _, PowerResourceManagerInterface::PowerResourceLevel::ACTIVE_HIGH)); + EXPECT_CALL(*m_mockAppPowerManager, create(_, _, PowerResourceManagerInterface::PowerResourceLevel::ACTIVE_LOW)); + + m_powerManager->create(TEST_ID, TEST_REFCOUNTED, TEST_LEVEL); + m_powerManager->create( + TEST_ID + "2", TEST_REFCOUNTED, PowerResourceManagerInterface::PowerResourceLevel::ACTIVE_HIGH); + m_powerManager->create( + TEST_ID + "3", TEST_REFCOUNTED, PowerResourceManagerInterface::PowerResourceLevel::ACTIVE_LOW); +} + +/** + * Test acquiring with multiple resources with the same level results in calls + * to the aggregated resource. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_acquireMultipleSameLevel) { + EXPECT_CALL(*m_mockAppPowerManager, acquire(_, _)).Times(3); + + auto resource1 = m_powerManager->create(TEST_ID, TEST_REFCOUNTED, TEST_LEVEL); + auto resource2 = m_powerManager->create(TEST_ID + "2", TEST_REFCOUNTED, TEST_LEVEL); + auto resource3 = m_powerManager->create(TEST_ID + "3", TEST_REFCOUNTED, TEST_LEVEL); + + m_powerManager->acquire(resource1); + m_powerManager->acquire(resource2); + m_powerManager->acquire(resource3); +} + +/** + * Test acquiring with multiple resources with different levels results in calls + * to the aggregated resource. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_acquireMultipleDifferentLevels) { + EXPECT_CALL(*m_mockAppPowerManager, acquire(_, _)).Times(3); + + auto resource1 = m_powerManager->create(TEST_ID, TEST_REFCOUNTED, TEST_LEVEL); + auto resource2 = m_powerManager->create( + TEST_ID + "2", TEST_REFCOUNTED, PowerResourceManagerInterface::PowerResourceLevel::ACTIVE_HIGH); + auto resource3 = m_powerManager->create( + TEST_ID + "3", TEST_REFCOUNTED, PowerResourceManagerInterface::PowerResourceLevel::ACTIVE_LOW); + + m_powerManager->acquire(resource1); + m_powerManager->acquire(resource2); + m_powerManager->acquire(resource3); +} + +/** + * Test acquiring with a nullptr. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_acquireNull) { + m_powerManager.reset(); + + auto strictPowerManager = std::make_shared>(); + m_powerManager = AggregatedPowerResourceManager::create(strictPowerManager); + + m_powerManager->acquire(nullptr); +} + +/** + * Test acquiring multiple refcounted resources results in multiple acquire. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_acquireRefCounted) { + EXPECT_CALL(*m_mockAppPowerManager, acquire(_, _)).Times(3); + + auto resource1 = m_powerManager->create(TEST_ID, TEST_REFCOUNTED, TEST_LEVEL); + + m_powerManager->acquire(resource1); + m_powerManager->acquire(resource1); + m_powerManager->acquire(resource1); +} + +/** + * Test acquiring multiple non-ref counted resources results in just one acquire. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_acquireNonRefCounted) { + EXPECT_CALL(*m_mockAppPowerManager, acquire(_, _)); + + auto resource1 = m_powerManager->create(TEST_ID, false, TEST_LEVEL); + + m_powerManager->acquire(resource1); + m_powerManager->acquire(resource1); + m_powerManager->acquire(resource1); +} + +/** + * Test release with multiple resources with the same level results in calls + * to the aggregated resource. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_releaseMultipleSameLevel) { + EXPECT_CALL(*m_mockAppPowerManager, release(_)).Times(3); + + auto resource1 = m_powerManager->create(TEST_ID, TEST_REFCOUNTED, TEST_LEVEL); + auto resource2 = m_powerManager->create(TEST_ID + "2", TEST_REFCOUNTED, TEST_LEVEL); + auto resource3 = m_powerManager->create(TEST_ID + "3", TEST_REFCOUNTED, TEST_LEVEL); + + m_powerManager->acquire(resource1); + m_powerManager->acquire(resource2); + m_powerManager->acquire(resource3); + + m_powerManager->release(resource1); + m_powerManager->release(resource2); + m_powerManager->release(resource3); +} + +/** + * Test release with multiple resources with different levels results in calls + * to the aggregated resource. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_releaseMultipleDifferentLevels) { + EXPECT_CALL(*m_mockAppPowerManager, release(_)).Times(3); + + auto resource1 = m_powerManager->create(TEST_ID, TEST_REFCOUNTED, TEST_LEVEL); + auto resource2 = m_powerManager->create( + TEST_ID + "2", TEST_REFCOUNTED, PowerResourceManagerInterface::PowerResourceLevel::ACTIVE_HIGH); + auto resource3 = m_powerManager->create( + TEST_ID + "3", TEST_REFCOUNTED, PowerResourceManagerInterface::PowerResourceLevel::ACTIVE_LOW); + + m_powerManager->acquire(resource1); + m_powerManager->acquire(resource2); + m_powerManager->acquire(resource3); + + m_powerManager->release(resource1); + m_powerManager->release(resource2); + m_powerManager->release(resource3); +} + +/** + * Test release with a nullptr. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_releaseNull) { + m_powerManager.reset(); + + auto strictPowerManager = std::make_shared>(); + m_powerManager = AggregatedPowerResourceManager::create(strictPowerManager); + + m_powerManager->release(nullptr); +} + +/** + * Test releasing multiple refcounted resources results in multiple release. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_releaseRefCounted) { + EXPECT_CALL(*m_mockAppPowerManager, release(_)).Times(3); + + auto resource1 = m_powerManager->create(TEST_ID, TEST_REFCOUNTED, TEST_LEVEL); + + m_powerManager->acquire(resource1); + m_powerManager->acquire(resource1); + m_powerManager->acquire(resource1); + + m_powerManager->release(resource1); + m_powerManager->release(resource1); + m_powerManager->release(resource1); +} + +/** + * Test releasing multiple non-ref counted resources results in just one acquire. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_releaseNonRefCounted) { + EXPECT_CALL(*m_mockAppPowerManager, acquire(_, _)); + + auto resource1 = m_powerManager->create(TEST_ID, false, TEST_LEVEL); + + m_powerManager->acquire(resource1); + m_powerManager->acquire(resource1); + m_powerManager->acquire(resource1); + + m_powerManager->release(resource1); + m_powerManager->release(resource1); + m_powerManager->release(resource1); +} + +/** + * Test closing a power resource results in releasing remaining refCount. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_close) { + EXPECT_CALL(*m_mockAppPowerManager, release(_)).Times(2); + auto resource1 = m_powerManager->create(TEST_ID, TEST_REFCOUNTED, TEST_LEVEL); + + m_powerManager->acquire(resource1); + m_powerManager->acquire(resource1); + m_powerManager->close(resource1); +} + +/** + * Test calling close will cleanup the aggregated resource if there are no more resources of that level. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_closeCleanup) { + EXPECT_CALL(*m_mockAppPowerManager, close(_)); + auto resource1 = m_powerManager->create(TEST_ID, TEST_REFCOUNTED, TEST_LEVEL); + m_powerManager->close(resource1); +} + +/** + * Test ensuring that acquirePowerResource calls the underlying application PowerResourceManagerInterface methods. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_acquirePowerResourceLegacy) { + EXPECT_CALL(*m_mockAppPowerManager, acquirePowerResource(TEST_ID, TEST_LEVEL)); + m_powerManager->acquirePowerResource(TEST_ID, TEST_LEVEL); +} + +/** + * Test ensuring that releasePowerResource calls the underlying application PowerResourceManagerInterface methods. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_releasePowerResourceLegacy) { + EXPECT_CALL(*m_mockAppPowerManager, releasePowerResource(TEST_ID)); + m_powerManager->releasePowerResource(TEST_ID); +} + +/** + * Test ensuring that isPowerResourceAcquired calls the underlying application PowerResourceManagerInterface methods. + */ +TEST_F(AggregatedPowerResourceManagerTest, test_isPowerResourceAcquiredLegacy) { + EXPECT_CALL(*m_mockAppPowerManager, isPowerResourceAcquired(TEST_ID)); + m_powerManager->isPowerResourceAcquired(TEST_ID); +} + +} // namespace test +} // namespace power +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/test/AVSCommon/Utils/Power/PowerResourceTest.cpp b/AVSCommon/Utils/test/AVSCommon/Utils/Power/PowerResourceTest.cpp new file mode 100644 index 0000000000..1c676afb1f --- /dev/null +++ b/AVSCommon/Utils/test/AVSCommon/Utils/Power/PowerResourceTest.cpp @@ -0,0 +1,323 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace power { +namespace test { + +using namespace ::testing; +using namespace avsCommon::sdkInterfaces; +using namespace avsCommon::sdkInterfaces::test; + +// Component name. +static std::string TEST_ID = "test"; + +// Component name. +static std::string PREFIXED_TEST_ID = std::string(PowerResource::PREFIX) + "test"; + +// Component level. +static PowerResourceManagerInterface::PowerResourceLevel TEST_LEVEL = + PowerResourceManagerInterface::PowerResourceLevel::STANDBY_LOW; + +// Component refCount configuration. +static bool TEST_REFCOUNTED = true; + +MATCHER_P( + HasPowerResourceId, + /* std::string */ id, + "") { + std::shared_ptr powerResourceId = arg; + if (!powerResourceId) { + return false; + } + + return id == powerResourceId->getResourceId(); +} + +class PowerResourceTest : public ::testing::Test { +public: + /// SetUp for the test. + void SetUp() override; + + /// PowerResourceManagerInterface Mock. + std::shared_ptr> m_powerManagerMock; + + /// PowerResource under test. + std::shared_ptr m_powerResource; +}; + +void PowerResourceTest::SetUp() { + m_powerManagerMock = std::make_shared>(); + m_powerManagerMock->setDefaultBehavior(); + + m_powerResource = PowerResource::create(TEST_ID, m_powerManagerMock, TEST_LEVEL, TEST_REFCOUNTED); +} + +/// Test that passing in a nullptr for PowerResourceManagerInterface results in a nullptr. +TEST_F(PowerResourceTest, test_createWithNull) { + auto noopPowerResource = PowerResource::create(TEST_ID, nullptr, TEST_LEVEL); + EXPECT_THAT(noopPowerResource, IsNull()); +} + +/// Test that basic getters work as expected. +TEST_F(PowerResourceTest, test_getters) { + EXPECT_EQ(m_powerResource->getId(), TEST_ID); + EXPECT_EQ(m_powerResource->getLevel(), TEST_LEVEL); + EXPECT_EQ(m_powerResource->isRefCounted(), TEST_REFCOUNTED); +} + +/// Test acquire calls the underlying @c PowerResourceManagerInterface. +TEST_F(PowerResourceTest, test_acquire) { + EXPECT_CALL(*m_powerManagerMock, acquire(HasPowerResourceId(PREFIXED_TEST_ID), _)); + m_powerResource->acquire(); +} + +/// Test multiple acquire calls reflect changes in refcount and the underlying @c PowerResourceManagerInterface being +/// called. +TEST_F(PowerResourceTest, test_multiAcquire) { + EXPECT_CALL(*m_powerManagerMock, acquire(HasPowerResourceId(PREFIXED_TEST_ID), _)).Times(2); + + for (unsigned int i = 0; i < 2; i++) { + m_powerResource->acquire(); + } +} + +/// Test destructor releases. +TEST_F(PowerResourceTest, test_destructor_releases) { + EXPECT_CALL(*m_powerManagerMock, release(HasPowerResourceId(PREFIXED_TEST_ID))).Times(2); + m_powerResource->acquire(); + m_powerResource->acquire(); + m_powerResource.reset(); +} + +/// Test release calls the underlying @c PowerResourceManagerInterface. +TEST_F(PowerResourceTest, test_releaseNoAcquireSucceeds) { + EXPECT_CALL(*m_powerManagerMock, release(HasPowerResourceId(PREFIXED_TEST_ID))); + m_powerResource->release(); +} + +/// Test release calls the underlying @c PowerResourceManagerInterface. +TEST_F(PowerResourceTest, test_release) { + { + InSequence s; + EXPECT_CALL(*m_powerManagerMock, acquire(HasPowerResourceId(PREFIXED_TEST_ID), _)); + EXPECT_CALL(*m_powerManagerMock, release(HasPowerResourceId(PREFIXED_TEST_ID))); + } + + m_powerResource->acquire(); + m_powerResource->release(); +} + +/// Test multiple releases result in the underlying @c PowerResourceManagerInterface being called, and the appropriate +/// refcount. +TEST_F(PowerResourceTest, test_multiReleaseSymmetricalSucceeds) { + { + InSequence s; + EXPECT_CALL(*m_powerManagerMock, acquire(HasPowerResourceId(PREFIXED_TEST_ID), _)).Times(AnyNumber()); + EXPECT_CALL(*m_powerManagerMock, release(HasPowerResourceId(PREFIXED_TEST_ID))).Times(AnyNumber()); + } + + m_powerResource->acquire(); + m_powerResource->acquire(); + + m_powerResource->release(); + m_powerResource->release(); +} + +/// Test multiple releases result in the underlying @c PowerResourceManagerInterface being called, and the appropriate +/// refcount. +TEST_F(PowerResourceTest, test_multiReleaseAssymmetricalSucceeds) { + { + InSequence s; + EXPECT_CALL(*m_powerManagerMock, acquire(HasPowerResourceId(PREFIXED_TEST_ID), _)); + EXPECT_CALL(*m_powerManagerMock, release(HasPowerResourceId(PREFIXED_TEST_ID))).Times(2); + } + + m_powerResource->acquire(); + m_powerResource->release(); + m_powerResource->release(); +} + +/// Test freeze calls underlying @c PowerResourceManagerInterface. +TEST_F(PowerResourceTest, test_freezeMultiSucceeds) { + { + InSequence s; + EXPECT_CALL(*m_powerManagerMock, acquire(HasPowerResourceId(PREFIXED_TEST_ID), _)).Times(AnyNumber()); + // Account for destructor release. + EXPECT_CALL(*m_powerManagerMock, release(HasPowerResourceId(PREFIXED_TEST_ID))).Times(4); + } + + m_powerResource->acquire(); + m_powerResource->acquire(); + m_powerResource->freeze(); + EXPECT_TRUE(m_powerResource->isFrozen()); + m_powerResource.reset(); +} + +/// Test acquiring a frozen resource fails. +TEST_F(PowerResourceTest, test_frozenAcquireFails) { + { + InSequence s; + EXPECT_CALL(*m_powerManagerMock, acquire(HasPowerResourceId(PREFIXED_TEST_ID), _)); + // Account for destructor release. + EXPECT_CALL(*m_powerManagerMock, release(HasPowerResourceId(PREFIXED_TEST_ID))).Times(2); + } + + m_powerResource->acquire(); + m_powerResource->freeze(); + EXPECT_TRUE(m_powerResource->isFrozen()); + + m_powerResource->acquire(); + EXPECT_TRUE(m_powerResource->isFrozen()); + m_powerResource.reset(); +} + +/// Test releasing a frozen resource fails. +TEST_F(PowerResourceTest, test_frozenReleaseFails) { + auto strictPowerManager = std::make_shared>(); + strictPowerManager->setDefaultBehavior(); + + EXPECT_CALL(*strictPowerManager, create(_, _, _)).Times(AnyNumber()); + EXPECT_CALL(*strictPowerManager, close(_)).Times(AnyNumber()); + + m_powerResource = PowerResource::create(TEST_ID, strictPowerManager, TEST_LEVEL, TEST_REFCOUNTED); + EXPECT_CALL(*strictPowerManager, acquire(HasPowerResourceId(PREFIXED_TEST_ID), _)); + // freeze() releases once. + // Account for destructor release. + EXPECT_CALL(*strictPowerManager, release(HasPowerResourceId(PREFIXED_TEST_ID))).Times(2); + + m_powerResource->acquire(); + m_powerResource->freeze(); + EXPECT_TRUE(m_powerResource->isFrozen()); + + m_powerResource->release(); + EXPECT_TRUE(m_powerResource->isFrozen()); + m_powerResource.reset(); +} + +/// Test multiple freeze calls keeps resource frozen and maintains the refcount. +TEST_F(PowerResourceTest, test_multipleFreezeIdempotent) { + // Account for destructor release. + EXPECT_CALL(*m_powerManagerMock, release(HasPowerResourceId(PREFIXED_TEST_ID))).Times(2); + + m_powerResource->acquire(); + m_powerResource->freeze(); + EXPECT_TRUE(m_powerResource->isFrozen()); + + m_powerResource->freeze(); + EXPECT_TRUE(m_powerResource->isFrozen()); + m_powerResource.reset(); +} + +/// Test thaw acquires as many refCounts as available. +TEST_F(PowerResourceTest, test_thawMulti) { + // Initial acquire + 2x thaw(). + EXPECT_CALL(*m_powerManagerMock, acquire(HasPowerResourceId(PREFIXED_TEST_ID), _)).Times(4); + + m_powerResource->acquire(); + m_powerResource->acquire(); + m_powerResource->freeze(); + m_powerResource->thaw(); +} + +/// Test acquiring a thawed resource succeeds. +TEST_F(PowerResourceTest, test_thawAcquireSucceeds) { + EXPECT_CALL(*m_powerManagerMock, acquire(HasPowerResourceId(PREFIXED_TEST_ID), _)); + + m_powerResource->freeze(); + m_powerResource->thaw(); + m_powerResource->acquire(); + + EXPECT_FALSE(m_powerResource->isFrozen()); +} + +/// Test releasing a thawed resource succeeds. +TEST_F(PowerResourceTest, test_thawReleaseSucceeds) { + { + InSequence s; + EXPECT_CALL(*m_powerManagerMock, acquire(HasPowerResourceId(PREFIXED_TEST_ID), _)); + EXPECT_CALL(*m_powerManagerMock, release(HasPowerResourceId(PREFIXED_TEST_ID))); + EXPECT_CALL(*m_powerManagerMock, acquire(HasPowerResourceId(PREFIXED_TEST_ID), _)); + EXPECT_CALL(*m_powerManagerMock, release(HasPowerResourceId(PREFIXED_TEST_ID))); + } + + m_powerResource->acquire(); + m_powerResource->freeze(); + m_powerResource->thaw(); + m_powerResource->release(); + + EXPECT_FALSE(m_powerResource->isFrozen()); +} + +/// Test multiple thaws results. +TEST_F(PowerResourceTest, test_multipleThawIdempotent) { + { + InSequence s; + EXPECT_CALL(*m_powerManagerMock, acquire(HasPowerResourceId(PREFIXED_TEST_ID), _)); + EXPECT_CALL(*m_powerManagerMock, release(HasPowerResourceId(PREFIXED_TEST_ID))); + // Single Thaw + EXPECT_CALL(*m_powerManagerMock, acquire(HasPowerResourceId(PREFIXED_TEST_ID), _)); + // Account for destructor. + EXPECT_CALL(*m_powerManagerMock, release(HasPowerResourceId(PREFIXED_TEST_ID))); + } + + m_powerResource->acquire(); + m_powerResource->freeze(); + + m_powerResource->thaw(); + EXPECT_FALSE(m_powerResource->isFrozen()); + + m_powerResource->thaw(); + EXPECT_FALSE(m_powerResource->isFrozen()); + m_powerResource.reset(); +} + +/// Test that acquire for a non-refCounted resource results in a refCount of 1. +TEST_F(PowerResourceTest, test_notRefCountedMultipleAcquireSuccceeds) { + m_powerResource = PowerResource::create(TEST_ID, m_powerManagerMock, TEST_LEVEL, !TEST_REFCOUNTED); + + EXPECT_CALL(*m_powerManagerMock, acquire(HasPowerResourceId(PREFIXED_TEST_ID), _)).Times(2); + + m_powerResource->acquire(); + m_powerResource->acquire(); +} + +/// Test that in a none refCounted resource release works. +TEST_F(PowerResourceTest, test_notRefCountedReleaseSucceeds) { + m_powerResource = PowerResource::create(TEST_ID, m_powerManagerMock, TEST_LEVEL, !TEST_REFCOUNTED); + + EXPECT_CALL(*m_powerManagerMock, release(HasPowerResourceId(PREFIXED_TEST_ID))).Times(2); + + m_powerResource->acquire(); + m_powerResource->acquire(); + m_powerResource->release(); + m_powerResource->release(); +} + +} // namespace test +} // namespace power +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/test/AVSCommon/Utils/Power/WakeGuardTest.cpp b/AVSCommon/Utils/test/AVSCommon/Utils/Power/WakeGuardTest.cpp new file mode 100644 index 0000000000..d82a318456 --- /dev/null +++ b/AVSCommon/Utils/test/AVSCommon/Utils/Power/WakeGuardTest.cpp @@ -0,0 +1,78 @@ +/* + * 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 + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace power { +namespace test { + +using namespace ::testing; +using namespace avsCommon::sdkInterfaces; +using namespace avsCommon::sdkInterfaces::test; + +// Component name. +static std::string TEST_ID = "test"; + +MATCHER_P( + ContainsPowerResourceId, + /* std::string */ id, + "") { + std::shared_ptr powerResourceId = arg; + if (!powerResourceId) { + return false; + } + + return std::string::npos != powerResourceId->getResourceId().find(id); +} + +/// Test that the constructor with a nullptr doesn't segfault. +TEST(WakeGuardTest, test_constructWithNull) { + WakeGuard wake(nullptr); +} + +TEST(WakeGuardTest, test_createAcquires) { + auto powerManagerMock = std::make_shared>(); + powerManagerMock->setDefaultBehavior(); + auto powerResource = PowerResource::create(TEST_ID, powerManagerMock); + + EXPECT_CALL(*powerManagerMock, acquire(ContainsPowerResourceId(TEST_ID), _)); + + auto wake = WakeGuard(powerResource); +} + +TEST(WakeGuardTest, test_destructorReleases) { + auto powerManagerMock = std::make_shared>(); + powerManagerMock->setDefaultBehavior(); + auto powerResource = PowerResource::create(TEST_ID, powerManagerMock); + + EXPECT_CALL(*powerManagerMock, release(ContainsPowerResourceId(TEST_ID))); + + { auto wake = WakeGuard(powerResource); } +} + +} // namespace test +} // namespace power +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK diff --git a/AVSCommon/Utils/test/AVSCommon/Utils/Threading/ConditionVariableWrapperTest.cpp b/AVSCommon/Utils/test/AVSCommon/Utils/Threading/ConditionVariableWrapperTest.cpp new file mode 100644 index 0000000000..b79aca3bd5 --- /dev/null +++ b/AVSCommon/Utils/test/AVSCommon/Utils/Threading/ConditionVariableWrapperTest.cpp @@ -0,0 +1,1019 @@ +/* + * 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. + */ + +#ifdef ENABLE_LPM + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { +namespace threading { +namespace test { + +using namespace ::testing; +using namespace avsCommon::sdkInterfaces; +using namespace avsCommon::sdkInterfaces::test; +using namespace avsCommon::utils::error; +using namespace avsCommon::utils::power; + +/// PowerResource Component name for thread. +static std::string TEST_THREAD_ID = "test-thread"; + +/// PowerResource Component name. +static std::string TEST_ID = "test"; + +/// Long timeout used to ensure an expected event does happen. +static std::chrono::minutes LONG_TIMEOUT{2}; + +/// Tolerance in time difference used for testing with LONG_TIMEOUT. +static std::chrono::minutes TIMEOUT_TOLERANCE{2}; + +/// Short timeout used to verify an event does not happen. +static std::chrono::milliseconds SHORT_TIMEOUT{750}; + +/** + * Due to the nature of the object under test, ConditionVariableWrapper, we must perform white-box testing + * to verify the correct notification and power management logic. + * + * These tests require PowerMonitor to be enabled. + */ +class ConditionVariableWrapperPowerTest : public ::testing::Test { +public: + virtual void SetUp() override; + virtual void TearDown() override; + + /** + * Checks notifyX methods against power expectations. + * + * Check that during wait thread PowerResources are released. + * Check that PowerResource are acquired in notifyAll. + * Check that there are non-zero PowerResources acquired in wait predicate. + * Check that PowerResources are thawed after wait predicate. + * + * @param numberOfThreads Number of threads to spawn. + * @param notify The notify methods to call. + */ + void notifyPowerCheck(int numberOfThreads, std::function notify); + + /// PowerResourceManagerInterface Mock. + std::shared_ptr> m_powerManagerMock; + + /// Mutex for synchronization. + std::mutex m_mutex; + + /** + * ConditionVariableWrapper. + * Use a pointer because we need to ensure this is instantiated + * only after PowerMonitor has been initialized with a @c PowerResourceMonitorInterface. + */ + std::shared_ptr m_cv; + + /// Track refcount for @c MockPowerResourceManager. + std::atomic m_totalRefCount; + + /// Variable to measure success. + bool m_exitCondition; + + /// Return value from waitFor/waitUntil operations. + bool m_waitReturn; + + /// Often tests require that m_cv be in the waiting state. + WaitEvent m_enteredWaiting; +}; + +/// These are tests that do not require PowerMonitor to be enabled. +class ConditionVariableWrapperTest + : public ConditionVariableWrapperPowerTest + , public testing::WithParamInterface { +public: + void SetUp() override; +}; + +void ConditionVariableWrapperPowerTest::SetUp() { + m_powerManagerMock = std::make_shared>(); + PowerMonitor::getInstance()->activate(m_powerManagerMock); + m_cv = std::make_shared(); + m_exitCondition = false; + m_totalRefCount = 0; + m_waitReturn = false; + + ON_CALL(*m_powerManagerMock, acquire(_, _)).WillByDefault(InvokeWithoutArgs([this] { + m_totalRefCount++; + return true; + })); + + ON_CALL(*m_powerManagerMock, release(_)).WillByDefault(InvokeWithoutArgs([this] { + if (m_totalRefCount < 1) { + return false; + } + m_totalRefCount--; + return true; + })); +} + +void ConditionVariableWrapperTest::SetUp() { + m_cv = std::make_shared(); + m_exitCondition = false; + m_totalRefCount = 0; + m_waitReturn = false; + + if (GetParam()) { + m_powerManagerMock = std::make_shared>(); + PowerMonitor::getInstance()->activate(m_powerManagerMock); + + ON_CALL(*m_powerManagerMock, acquire(_, _)).WillByDefault(InvokeWithoutArgs([this] { + m_totalRefCount++; + return true; + })); + ON_CALL(*m_powerManagerMock, release(_)).WillByDefault(InvokeWithoutArgs([this] { + if (m_totalRefCount < 1) { + return false; + } + m_totalRefCount--; + return true; + })); + } +} + +void ConditionVariableWrapperPowerTest::TearDown() { + PowerMonitor::getInstance()->deactivate(); + m_cv.reset(); +} + +INSTANTIATE_TEST_CASE_P(TogglePowerMonitor, ConditionVariableWrapperTest, testing::Values(true, false)); + +void ConditionVariableWrapperPowerTest::notifyPowerCheck(int numberOfThreads, std::function notify) { + std::vector threadMonikers; + std::vector> threads; + // Check each thread's PowerResource. + std::map> waitingThreadPRs; + std::vector> acquireThreadIds; + + // Only proceed if all threads under test are waiting. + std::condition_variable waitCV; + std::mutex waitMutex; + + for (int i = 0; i < numberOfThreads; i++) { + threadMonikers.push_back(TEST_THREAD_ID + std::to_string(i)); + } + + auto threadLogic = [this, &waitingThreadPRs, &waitMutex, &waitCV](std::string moniker) { + auto threadPR = PowerMonitor::getInstance()->getThreadPowerResourceOrCreate(moniker); + threadPR->acquire(); + + std::unique_lock lock(m_mutex); + waitingThreadPRs.insert({moniker, threadPR}); + m_cv->wait(lock, [this, &waitMutex, &waitCV] { + { + std::lock_guard lock(waitMutex); + waitCV.notify_one(); + } + + EXPECT_GT(m_totalRefCount, 0); + return m_exitCondition; + }); + }; + + for (unsigned int i = 0; i < threadMonikers.size(); i++) { + auto threadMoniker = threadMonikers.at(i); + auto thread = std::make_shared(threadLogic, threadMoniker); + threads.push_back(thread); + } + + { + std::unique_lock lock(waitMutex); + ASSERT_TRUE(waitCV.wait_for(lock, LONG_TIMEOUT, [&waitingThreadPRs, numberOfThreads] { + return waitingThreadPRs.size() == static_cast(numberOfThreads); + })); + } + + { + std::unique_lock lock(m_mutex); + ASSERT_TRUE(waitingThreadPRs.size() == threads.size()); + ASSERT_TRUE(waitingThreadPRs.size() == threadMonikers.size()); + + for (auto monikerToThreadPR : waitingThreadPRs) { + EXPECT_TRUE(monikerToThreadPR.second->isFrozen()); + } + EXPECT_EQ(m_totalRefCount, 0); + m_exitCondition = true; + } + + std::thread::id notifyingThreadId = std::this_thread::get_id(); + unsigned int acquiresOnNotifyingThread = 0; + + EXPECT_CALL(*m_powerManagerMock, acquire(_, _)) + .Times(AnyNumber()) + .WillRepeatedly(InvokeWithoutArgs([this, &acquiresOnNotifyingThread, notifyingThreadId] { + m_totalRefCount++; + if (std::this_thread::get_id() == notifyingThreadId) { + acquiresOnNotifyingThread++; + } + + return true; + })); + + notify(); + + for (auto thread : threads) { + thread->join(); + } + + // Check that we have acquires on the notifying thread during NotifyOne/All call. + EXPECT_GT(acquiresOnNotifyingThread, 0U); + + // Check that resources are thawed after wait. + for (auto monikerToThreadPR : waitingThreadPRs) { + auto threadPR = monikerToThreadPR.second; + EXPECT_FALSE(threadPR->isFrozen()); + } + + // m_totalRefCount cannot be negative so this cast is safe. + EXPECT_EQ(static_cast(m_totalRefCount), waitingThreadPRs.size()); +} + +// ------------- NotifyOne + +/// Test wait() succeeds. +TEST_P(ConditionVariableWrapperTest, test_wait_NoThreadPowerResource_NotifyOne_Succeeds) { + auto testPredicate = [this] { + std::unique_lock lock(m_mutex); + m_cv->wait(lock, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + m_exitCondition = true; + } + + m_cv->notifyOne(); + t1.join(); +} + +/// Test waitFor() succeeds and that no timeout happens. No thread PowerResource. +TEST_P(ConditionVariableWrapperTest, test_waitFor_NoThreadPowerResource_NotifyOne_Succeeds) { + int elapsedMs = 0; + + auto testPredicate = [this, &elapsedMs] { + std::unique_lock lock(m_mutex); + auto start = std::chrono::steady_clock::now(); + m_waitReturn = m_cv->waitFor(lock, LONG_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + auto stop = std::chrono::steady_clock::now(); + + elapsedMs = std::chrono::duration_cast(stop - start).count(); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + m_exitCondition = true; + } + + m_cv->notifyOne(); + t1.join(); + + EXPECT_LE(elapsedMs, TIMEOUT_TOLERANCE.count()); + EXPECT_TRUE(m_waitReturn); +} + +/// Test waitUntil() succeeds and that that timeout did not happen. No thread PowerResource. +TEST_P(ConditionVariableWrapperTest, test_waitUntil_NoThreadPowerResource_NotifyOne_Succeeds) { + int elapsedMs = 0; + + auto testPredicate = [this, &elapsedMs] { + std::unique_lock lock(m_mutex); + auto start = std::chrono::steady_clock::now(); + m_waitReturn = m_cv->waitUntil(lock, start + LONG_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + auto stop = std::chrono::steady_clock::now(); + + elapsedMs = std::chrono::duration_cast(stop - start).count(); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + m_exitCondition = true; + } + + m_cv->notifyOne(); + t1.join(); + + EXPECT_LE(elapsedMs, TIMEOUT_TOLERANCE.count()); + EXPECT_TRUE(m_waitReturn); +} + +/// Check that calling notifyOne with no threads waiting does not result in the resource being held. +TEST_P(ConditionVariableWrapperTest, test_notifyOne_NoWaitingThreads_PowerCheck) { + m_cv->notifyOne(); + EXPECT_EQ(m_totalRefCount, 0); +} + +/// Test wait() succeeds and that timeout did not happen. With thread PowerResource. +TEST_F(ConditionVariableWrapperPowerTest, test_wait_ThreadPowerResource_NotifyOne_Succeeds) { + std::shared_ptr waitingThreadPR; + auto testPredicate = [this, &waitingThreadPR] { + waitingThreadPR = PowerMonitor::getInstance()->getThreadPowerResourceOrCreate(TEST_THREAD_ID); + waitingThreadPR->acquire(); + + std::unique_lock lock(m_mutex); + m_cv->wait(lock, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + ASSERT_TRUE(waitingThreadPR); + EXPECT_TRUE(waitingThreadPR->isFrozen()); + m_exitCondition = true; + } + + m_cv->notifyOne(); + t1.join(); + + EXPECT_EQ(m_totalRefCount, 1); + EXPECT_FALSE(waitingThreadPR->isFrozen()); +} + +/// Test waitFor() succeeds and that that timeout did not happen. With thread PowerResource. +TEST_F(ConditionVariableWrapperPowerTest, test_waitFor_ThreadPowerResource_NotifyOne_Succeeds) { + int elapsedMs = 0; + std::shared_ptr waitingThreadPR; + + auto testPredicate = [this, &waitingThreadPR, &elapsedMs] { + waitingThreadPR = PowerMonitor::getInstance()->getThreadPowerResourceOrCreate(TEST_THREAD_ID); + waitingThreadPR->acquire(); + + std::unique_lock lock(m_mutex); + auto start = std::chrono::steady_clock::now(); + m_waitReturn = m_cv->waitFor(lock, LONG_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + auto stop = std::chrono::steady_clock::now(); + + elapsedMs = std::chrono::duration_cast(stop - start).count(); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + ASSERT_TRUE(waitingThreadPR); + EXPECT_TRUE(waitingThreadPR->isFrozen()); + m_exitCondition = true; + } + + m_cv->notifyOne(); + t1.join(); + + EXPECT_LE(elapsedMs, TIMEOUT_TOLERANCE.count()); + EXPECT_EQ(m_totalRefCount, 1); + EXPECT_FALSE(waitingThreadPR->isFrozen()); + EXPECT_TRUE(m_waitReturn); +} + +/// Test waitUntil() succeeds and that that timeout did not happen. With thread PowerResource. +TEST_F(ConditionVariableWrapperPowerTest, test_waitUntil_ThreadPowerResource_NotifyOne_Succeeds) { + int elapsedMs = 0; + std::shared_ptr waitingThreadPR; + + auto testPredicate = [this, &waitingThreadPR, &elapsedMs] { + waitingThreadPR = PowerMonitor::getInstance()->getThreadPowerResourceOrCreate(TEST_THREAD_ID); + waitingThreadPR->acquire(); + + std::unique_lock lock(m_mutex); + auto start = std::chrono::steady_clock::now(); + m_waitReturn = m_cv->waitUntil(lock, start + LONG_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + auto stop = std::chrono::steady_clock::now(); + + elapsedMs = std::chrono::duration_cast(stop - start).count(); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + ASSERT_TRUE(waitingThreadPR); + EXPECT_TRUE(waitingThreadPR->isFrozen()); + m_exitCondition = true; + } + + m_cv->notifyOne(); + t1.join(); + + EXPECT_LE(elapsedMs, TIMEOUT_TOLERANCE.count()); + EXPECT_EQ(m_totalRefCount, 1); + EXPECT_FALSE(waitingThreadPR->isFrozen()); + EXPECT_TRUE(m_waitReturn); +} + +/** + * Test wait() succeeds with strict checks against PowerResourceManagerInterface to ensure we don't + * accidentally enter a low power state. + */ +TEST_F(ConditionVariableWrapperPowerTest, test_wait_ThreadPowerResource_NotifyOne_PowerCheck_Succeeds) { + const int NUM_THREADS = 1; + notifyPowerCheck(NUM_THREADS, [this] { + for (int i = 0; i < NUM_THREADS; i++) { + m_cv->notifyOne(); + } + }); +} + +/** + * Test wait() succeeds with strict checks against PowerResourceManagerInterface to ensure we don't accidentally enter a + * low power state. + * + * Call notifyOne() in succession on each thread. + */ +TEST_F(ConditionVariableWrapperPowerTest, test_wait_ThreadPowerResource_NotifyOne_PowerCheckMultiple_Succeeds) { + const int NUM_THREADS = 3; + notifyPowerCheck(NUM_THREADS, [this] { + for (int i = 0; i < NUM_THREADS; i++) { + m_cv->notifyOne(); + } + }); +} + +/** + * Test when notifyOne() is called and the predicate is false. Expect the resource to be frozen. + * This test uses strict timing because we want to measure the state after notifyOne() is called, + * the predicate has been evaluated to false, and the thread goes back to being blocked. In this state, + * we verify if the resource has been frozen. + */ +TEST_F( + ConditionVariableWrapperPowerTest, + test_wait_ThreadPowerResource_NotifyOne_PowerCheckFalsePredicate_ReleasesPower) { + WaitEvent waitForNotifyOne; + bool notifyOneCalled = false; + + std::shared_ptr waitingThreadPR; + auto threadLogic = [this, &waitingThreadPR, &waitForNotifyOne, ¬ifyOneCalled](std::string moniker) { + waitingThreadPR = PowerMonitor::getInstance()->getThreadPowerResourceOrCreate(moniker); + waitingThreadPR->acquire(); + + std::unique_lock lock(m_mutex); + m_cv->wait(lock, [this, &waitForNotifyOne, ¬ifyOneCalled] { + m_enteredWaiting.wakeUp(); + if (notifyOneCalled) { + waitForNotifyOne.wakeUp(); + } + EXPECT_GT(m_totalRefCount, 0); + return m_exitCondition; + }); + }; + + std::thread t1(threadLogic, TEST_THREAD_ID); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + EXPECT_EQ(m_totalRefCount, 0); + notifyOneCalled = true; + } + + m_cv->notifyOne(); + waitForNotifyOne.wait(LONG_TIMEOUT); + + { + std::lock_guard lock(m_mutex); + ASSERT_TRUE(waitingThreadPR); + EXPECT_EQ(waitingThreadPR->isFrozen(), true); + m_exitCondition = true; + } + + m_cv->notifyOne(); + t1.join(); +} + +// ------------- NotifyAll + +/// Test wait() succeeds. +TEST_P(ConditionVariableWrapperTest, test_wait_NoThreadPowerResource_NotifyAll_Succeeds) { + auto testPredicate = [this] { + std::unique_lock lock(m_mutex); + m_cv->wait(lock, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + m_exitCondition = true; + } + + m_cv->notifyAll(); + t1.join(); +} + +/// Test waitFor() succeeds and that timeout did not happen. No thread PowerResource. +TEST_P(ConditionVariableWrapperTest, test_waitFor_NoThreadPowerResource_NotifyAll_Succeeds) { + int elapsedMs = 0; + + auto testPredicate = [this, &elapsedMs] { + std::unique_lock lock(m_mutex); + auto start = std::chrono::steady_clock::now(); + m_waitReturn = m_cv->waitFor(lock, LONG_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + auto stop = std::chrono::steady_clock::now(); + + elapsedMs = std::chrono::duration_cast(stop - start).count(); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + m_exitCondition = true; + } + + m_cv->notifyAll(); + t1.join(); + + EXPECT_LE(elapsedMs, TIMEOUT_TOLERANCE.count()); + EXPECT_TRUE(m_waitReturn); +} + +/// Test waitUntil() succeeds and that that timeout did not happen. No thread PowerResource. +TEST_P(ConditionVariableWrapperTest, test_waitUntil_NoThreadPowerResource_NotifyAll_Succeeds) { + int elapsedMs = 0; + + auto testPredicate = [this, &elapsedMs] { + std::unique_lock lock(m_mutex); + auto start = std::chrono::steady_clock::now(); + m_waitReturn = m_cv->waitUntil(lock, start + LONG_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + auto stop = std::chrono::steady_clock::now(); + + elapsedMs = std::chrono::duration_cast(stop - start).count(); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + m_exitCondition = true; + } + + m_cv->notifyAll(); + t1.join(); + + EXPECT_LE(elapsedMs, TIMEOUT_TOLERANCE.count()); + EXPECT_TRUE(m_waitReturn); +} + +/// Check that calling notifyOne with no threads waiting does not result in the resource being held. +TEST_P(ConditionVariableWrapperTest, test_notifyAll_noWaitingThreads_PowerCheck) { + m_cv->notifyAll(); + EXPECT_EQ(m_totalRefCount, 0); +} + +/// Test wait() succeeds and that that timeout did not happen. With thread PowerResource. +TEST_F(ConditionVariableWrapperPowerTest, test_wait_ThreadPowerResource_NotifyAll_Succeeds) { + std::shared_ptr waitingThreadPR; + auto testPredicate = [this, &waitingThreadPR] { + waitingThreadPR = PowerMonitor::getInstance()->getThreadPowerResourceOrCreate(TEST_THREAD_ID); + waitingThreadPR->acquire(); + + std::unique_lock lock(m_mutex); + m_cv->wait(lock, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + ASSERT_TRUE(waitingThreadPR); + EXPECT_TRUE(waitingThreadPR->isFrozen()); + m_exitCondition = true; + } + + m_cv->notifyAll(); + t1.join(); + + EXPECT_EQ(m_totalRefCount, 1); + EXPECT_FALSE(waitingThreadPR->isFrozen()); +} + +/// Test waitUntil() succeeds and that timeout did not happen. With thread PowerResource. +TEST_F(ConditionVariableWrapperPowerTest, test_waitFor_ThreadPowerResource_NotifyAll_Succeeds) { + int elapsedMs = 0; + std::shared_ptr waitingThreadPR; + + auto testPredicate = [this, &waitingThreadPR, &elapsedMs] { + waitingThreadPR = PowerMonitor::getInstance()->getThreadPowerResourceOrCreate(TEST_THREAD_ID); + waitingThreadPR->acquire(); + + std::unique_lock lock(m_mutex); + auto start = std::chrono::steady_clock::now(); + m_waitReturn = m_cv->waitFor(lock, LONG_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + auto stop = std::chrono::steady_clock::now(); + + elapsedMs = std::chrono::duration_cast(stop - start).count(); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + ASSERT_TRUE(waitingThreadPR); + EXPECT_TRUE(waitingThreadPR->isFrozen()); + m_exitCondition = true; + } + + m_cv->notifyAll(); + t1.join(); + + EXPECT_LE(elapsedMs, TIMEOUT_TOLERANCE.count()); + EXPECT_EQ(m_totalRefCount, 1); + EXPECT_FALSE(waitingThreadPR->isFrozen()); + EXPECT_TRUE(m_waitReturn); +} + +/// Test waitUntil() succeeds and that that timeout did not happen. With thread PowerResource. +TEST_F(ConditionVariableWrapperPowerTest, test_waitUntil_ThreadPowerResource_NotifyAll_Succeeds) { + int elapsedMs = 0; + std::shared_ptr waitingThreadPR; + + auto testPredicate = [this, &waitingThreadPR, &elapsedMs] { + waitingThreadPR = PowerMonitor::getInstance()->getThreadPowerResourceOrCreate(TEST_THREAD_ID); + waitingThreadPR->acquire(); + + std::unique_lock lock(m_mutex); + auto start = std::chrono::steady_clock::now(); + m_waitReturn = m_cv->waitUntil(lock, start + LONG_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + auto stop = std::chrono::steady_clock::now(); + + elapsedMs = std::chrono::duration_cast(stop - start).count(); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + ASSERT_TRUE(waitingThreadPR); + EXPECT_TRUE(waitingThreadPR->isFrozen()); + m_exitCondition = true; + } + + m_cv->notifyAll(); + t1.join(); + + EXPECT_LE(elapsedMs, TIMEOUT_TOLERANCE.count()); + EXPECT_EQ(m_totalRefCount, 1); + EXPECT_FALSE(waitingThreadPR->isFrozen()); +} + +/** + * Test wait() succeeds with strict checks against PowerResourceManagerInterface to ensure we don't accidentally + * enter a low power state. Single blocked thread. + */ +TEST_F(ConditionVariableWrapperPowerTest, test_wait_ThreadPowerResource_NotifyAll_PowerCheck_Succeeds) { + notifyPowerCheck(1, [this] { m_cv->notifyAll(); }); +} + +/** + * Test wait() succeeds with strict checks against PowerResourceManagerInterface to ensure we don't accidentally + * enter a low power state. Multiple blocked threads. + */ +TEST_F(ConditionVariableWrapperPowerTest, test_wait_ThreadPowerResource_NotifyAll_PowerCheckMultiple_Succeeds) { + notifyPowerCheck(3, [this] { m_cv->notifyAll(); }); +} + +/** + * Test when notifyAll() is called and the predicate is false. Expect the resource to be frozen. + * This test uses strict timing because we want to measure the state after notifyAll() is called, + * the predicate has been evaluated to false, and the thread goes back to being blocked. In this state, + * we verify if the resource has been frozen. + */ +TEST_F( + ConditionVariableWrapperPowerTest, + test_wait_ThreadPowerResource_NotifyAll_PowerCheckFalsePredicate_ReleasesPower) { + WaitEvent waitForNotifyAll; + bool notifyAllCalled = false; + + std::shared_ptr waitingThreadPR; + auto threadLogic = [this, &waitingThreadPR, &waitForNotifyAll, ¬ifyAllCalled](std::string moniker) { + waitingThreadPR = PowerMonitor::getInstance()->getThreadPowerResourceOrCreate(moniker); + waitingThreadPR->acquire(); + + std::unique_lock lock(m_mutex); + m_cv->wait(lock, [this, &waitForNotifyAll, ¬ifyAllCalled] { + m_enteredWaiting.wakeUp(); + if (notifyAllCalled) { + waitForNotifyAll.wakeUp(); + } + EXPECT_GT(m_totalRefCount, 0); + return m_exitCondition; + }); + }; + + std::thread t1(threadLogic, TEST_THREAD_ID); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + EXPECT_EQ(m_totalRefCount, 0); + notifyAllCalled = true; + } + + m_cv->notifyAll(); + waitForNotifyAll.wait(LONG_TIMEOUT); + + { + std::lock_guard lock(m_mutex); + ASSERT_TRUE(waitingThreadPR); + EXPECT_EQ(waitingThreadPR->isFrozen(), true); + m_exitCondition = true; + } + + m_cv->notifyAll(); + t1.join(); +} + +// ------------- NoNotify + +/// Test waitFor() times out and returns false. Test no deadlocks. No thread PowerResource. +TEST_P(ConditionVariableWrapperTest, testTimer_waitFor_NoThreadPowerResource_NoNotify_ReturnsFalse) { + auto testPredicate = [this] { + std::unique_lock lock(m_mutex); + m_waitReturn = m_cv->waitFor(lock, SHORT_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + t1.join(); + + EXPECT_FALSE(m_waitReturn); +} + +/// Test waitUntil() times out and returns false. Test no deadlocks. No thread PowerResource. +TEST_P(ConditionVariableWrapperTest, testTimer_waitUntil_NoThreadPowerResource_NoNotify_ReturnsFalse) { + auto testPredicate = [this] { + std::unique_lock lock(m_mutex); + m_waitReturn = m_cv->waitUntil(lock, std::chrono::steady_clock::now() + SHORT_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + m_cv->notifyOne(); + t1.join(); + + EXPECT_FALSE(m_waitReturn); +} + +/// Test waitFor() times out and returns false. Test no deadlocks. No Power Monitor. +TEST_P(ConditionVariableWrapperTest, testTimer_waitFor_NoPowerMonitor_NoNotify_ReturnsFalse) { + PowerMonitor::getInstance()->deactivate(); + m_cv = std::make_shared(); + + auto testPredicate = [this] { + std::unique_lock lock(m_mutex); + m_waitReturn = m_cv->waitFor(lock, SHORT_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + t1.join(); + + EXPECT_FALSE(m_waitReturn); +} + +/// Test waitUntil() times out and returns false. Test no deadlocks. No Power Monitor. +TEST_P(ConditionVariableWrapperTest, testTimer_waitUntil_NoPowerMonitor_NoNotify_ReturnsFalse) { + PowerMonitor::getInstance()->deactivate(); + m_cv = std::make_shared(); + + auto testPredicate = [this] { + std::unique_lock lock(m_mutex); + m_waitReturn = m_cv->waitUntil(lock, std::chrono::steady_clock::now() + SHORT_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + t1.join(); + + EXPECT_FALSE(m_waitReturn); +} + +/// Test waitFor() times out and returns false. No deadlocks. With thread PowerResource. +TEST_F(ConditionVariableWrapperPowerTest, testTimer_waitFor_ThreadPowerResource_NoNotify_ReturnsFalse) { + std::shared_ptr waitingThreadPR; + + auto testPredicate = [this, &waitingThreadPR] { + waitingThreadPR = PowerMonitor::getInstance()->getThreadPowerResourceOrCreate(TEST_THREAD_ID); + waitingThreadPR->acquire(); + + std::unique_lock lock(m_mutex); + m_waitReturn = m_cv->waitFor(lock, SHORT_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + ASSERT_TRUE(waitingThreadPR); + EXPECT_TRUE(waitingThreadPR->isFrozen()); + } + + t1.join(); + + EXPECT_FALSE(waitingThreadPR->isFrozen()); + EXPECT_FALSE(m_waitReturn); +} + +/// Test waitFor() times out returns true if the predicate is true. No deadlocks. With thread PowerResource. +TEST_F(ConditionVariableWrapperPowerTest, testTimer_waitFor_ThreadPowerResource_NoNotifySuccessfulPred_ReturnsTrue) { + std::shared_ptr waitingThreadPR; + + auto testPredicate = [this, &waitingThreadPR] { + waitingThreadPR = PowerMonitor::getInstance()->getThreadPowerResourceOrCreate(TEST_THREAD_ID); + waitingThreadPR->acquire(); + + std::unique_lock lock(m_mutex); + m_waitReturn = m_cv->waitFor(lock, SHORT_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + ASSERT_TRUE(waitingThreadPR); + EXPECT_TRUE(waitingThreadPR->isFrozen()); + m_exitCondition = true; + } + + t1.join(); + + EXPECT_FALSE(waitingThreadPR->isFrozen()); + EXPECT_TRUE(m_waitReturn); +} + +/// Test waitUntil() times out and returns false. No deadlocks. With thread PowerResource. +TEST_F(ConditionVariableWrapperPowerTest, testTimer_waitUntil_ThreadPowerResource_NoNotify_ReturnsFalse) { + std::shared_ptr waitingThreadPR; + + auto testPredicate = [this, &waitingThreadPR] { + waitingThreadPR = PowerMonitor::getInstance()->getThreadPowerResourceOrCreate(TEST_THREAD_ID); + waitingThreadPR->acquire(); + + std::unique_lock lock(m_mutex); + m_waitReturn = m_cv->waitUntil(lock, std::chrono::steady_clock::now() + SHORT_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + + { + std::lock_guard lock(m_mutex); + ASSERT_TRUE(waitingThreadPR); + EXPECT_TRUE(waitingThreadPR->isFrozen()); + } + + t1.join(); + + EXPECT_FALSE(waitingThreadPR->isFrozen()); + EXPECT_FALSE(m_waitReturn); +} + +/// Test waitUntil() times out returns true if the predicate is true. No deadlocks. With thread PowerResource. +TEST_F(ConditionVariableWrapperPowerTest, testTimer_waitUntil_ThreadPowerResource_NoNotifySuccessfulPred_ReturnsTrue) { + std::shared_ptr waitingThreadPR; + + auto testPredicate = [this, &waitingThreadPR] { + waitingThreadPR = PowerMonitor::getInstance()->getThreadPowerResourceOrCreate(TEST_THREAD_ID); + waitingThreadPR->acquire(); + + std::unique_lock lock(m_mutex); + m_waitReturn = m_cv->waitUntil(lock, std::chrono::steady_clock::now() + SHORT_TIMEOUT, [this] { + m_enteredWaiting.wakeUp(); + return m_exitCondition; + }); + }; + + std::thread t1(testPredicate); + EXPECT_TRUE(m_enteredWaiting.wait(LONG_TIMEOUT)); + { + std::lock_guard lock(m_mutex); + ASSERT_TRUE(waitingThreadPR); + EXPECT_TRUE(waitingThreadPR->isFrozen()); + m_exitCondition = true; + } + + t1.join(); + + EXPECT_FALSE(waitingThreadPR->isFrozen()); + EXPECT_TRUE(m_waitReturn); +} + +int main(int argc, char** argv) { + return 1; +} + +} // namespace test +} // namespace threading +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK + +#endif diff --git a/AVSCommon/Utils/test/EndianTest.cpp b/AVSCommon/Utils/test/EndianTest.cpp new file mode 100644 index 0000000000..cfb33b0464 --- /dev/null +++ b/AVSCommon/Utils/test/EndianTest.cpp @@ -0,0 +1,44 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include + +#include "AVSCommon/Utils/Endian.h" + +namespace alexaClientSDK { +namespace avsCommon { +namespace utils { + +using namespace ::testing; + +class EndianTest : public Test {}; + +TEST_F(EndianTest, test_SwapEndian16) { + uint16_t AB = 0x4142; + uint16_t BA = 0x4241; + + EXPECT_EQ(AB, swapEndian(BA)); +} + +TEST_F(EndianTest, test_SwapEndian32) { + uint32_t ABCD = 0x41424344; + uint32_t DCBA = 0x44434241; + + EXPECT_EQ(ABCD, swapEndian(DCBA)); +} + +} // namespace utils +} // namespace avsCommon +} // namespace alexaClientSDK \ No newline at end of file diff --git a/AVSCommon/Utils/test/ExecutorTest.cpp b/AVSCommon/Utils/test/ExecutorTest.cpp index 255dafb31b..dca3cf4517 100644 --- a/AVSCommon/Utils/test/ExecutorTest.cpp +++ b/AVSCommon/Utils/test/ExecutorTest.cpp @@ -31,39 +31,39 @@ class ExecutorTest : public ::testing::Test { Executor executor; }; -TEST_F(ExecutorTest, test_submitStdFunctionAndVerifyExecution) { +TEST_F(ExecutorTest, testTimer_submitStdFunctionAndVerifyExecution) { std::function function = []() {}; auto future = executor.submit(function); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); } -TEST_F(ExecutorTest, test_submitStdBindAndVerifyExecution) { +TEST_F(ExecutorTest, testTimer_submitStdBindAndVerifyExecution) { auto future = executor.submit(std::bind(exampleFunctionParams, 0)); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); } -TEST_F(ExecutorTest, test_submitLambdaAndVerifyExecution) { +TEST_F(ExecutorTest, testTimer_submitLambdaAndVerifyExecution) { auto future = executor.submit([]() {}); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); } -TEST_F(ExecutorTest, test_submitFunctionPointerAndVerifyExecution) { +TEST_F(ExecutorTest, testTimer_submitFunctionPointerAndVerifyExecution) { auto future = executor.submit(&exampleFunction); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); } -TEST_F(ExecutorTest, test_submitFunctorAndVerifyExecution) { +TEST_F(ExecutorTest, testTimer_submitFunctorAndVerifyExecution) { ExampleFunctor exampleFunctor; auto future = executor.submit(exampleFunctor); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); } -TEST_F(ExecutorTest, test_submitFunctionWithPrimitiveReturnTypeNoArgsAndVerifyExecution) { +TEST_F(ExecutorTest, testTimer_submitFunctionWithPrimitiveReturnTypeNoArgsAndVerifyExecution) { int value = VALUE; auto future = executor.submit([=]() { return value; }); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); @@ -71,7 +71,7 @@ TEST_F(ExecutorTest, test_submitFunctionWithPrimitiveReturnTypeNoArgsAndVerifyEx ASSERT_EQ(future.get(), value); } -TEST_F(ExecutorTest, test_submitFunctionWithObjectReturnTypeNoArgsAndVerifyExecution) { +TEST_F(ExecutorTest, testTimer_submitFunctionWithObjectReturnTypeNoArgsAndVerifyExecution) { SimpleObject value(VALUE); auto future = executor.submit([=]() { return value; }); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); @@ -79,21 +79,21 @@ TEST_F(ExecutorTest, test_submitFunctionWithObjectReturnTypeNoArgsAndVerifyExecu ASSERT_EQ(future.get().getValue(), value.getValue()); } -TEST_F(ExecutorTest, test_submitFunctionWithNoReturnTypePrimitiveArgsAndVerifyExecution) { +TEST_F(ExecutorTest, testTimer_submitFunctionWithNoReturnTypePrimitiveArgsAndVerifyExecution) { int value = VALUE; auto future = executor.submit([](int number) {}, value); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); } -TEST_F(ExecutorTest, test_submitFunctionWithNoReturnTypeObjectArgsAndVerifyExecution) { +TEST_F(ExecutorTest, testTimer_submitFunctionWithNoReturnTypeObjectArgsAndVerifyExecution) { SimpleObject arg(0); auto future = executor.submit([](SimpleObject object) {}, arg); auto future_status = future.wait_for(SHORT_TIMEOUT_MS); ASSERT_EQ(future_status, std::future_status::ready); } -TEST_F(ExecutorTest, test_submitFunctionWithPrimitiveReturnTypeObjectArgsAndVerifyExecution) { +TEST_F(ExecutorTest, testTimer_submitFunctionWithPrimitiveReturnTypeObjectArgsAndVerifyExecution) { int value = VALUE; SimpleObject arg(0); auto future = executor.submit([=](SimpleObject object) { return value; }, arg); @@ -102,7 +102,7 @@ TEST_F(ExecutorTest, test_submitFunctionWithPrimitiveReturnTypeObjectArgsAndVeri ASSERT_EQ(future.get(), value); } -TEST_F(ExecutorTest, test_submitFunctionWithObjectReturnTypePrimitiveArgsAndVerifyExecution) { +TEST_F(ExecutorTest, testTimer_submitFunctionWithObjectReturnTypePrimitiveArgsAndVerifyExecution) { int arg = 0; SimpleObject value(VALUE); auto future = executor.submit([=](int primitive) { return value; }, arg); @@ -111,7 +111,7 @@ TEST_F(ExecutorTest, test_submitFunctionWithObjectReturnTypePrimitiveArgsAndVeri ASSERT_EQ(future.get().getValue(), value.getValue()); } -TEST_F(ExecutorTest, test_submitFunctionWithPrimitiveReturnTypePrimitiveArgsAndVerifyExecution) { +TEST_F(ExecutorTest, testTimer_submitFunctionWithPrimitiveReturnTypePrimitiveArgsAndVerifyExecution) { int arg = 0; int value = VALUE; auto future = executor.submit([=](int number) { return value; }, arg); @@ -120,7 +120,7 @@ TEST_F(ExecutorTest, test_submitFunctionWithPrimitiveReturnTypePrimitiveArgsAndV ASSERT_EQ(future.get(), value); } -TEST_F(ExecutorTest, test_submitFunctionWithObjectReturnTypeObjectArgsAndVerifyExecution) { +TEST_F(ExecutorTest, testTimer_submitFunctionWithObjectReturnTypeObjectArgsAndVerifyExecution) { SimpleObject value(VALUE); SimpleObject arg(0); auto future = executor.submit([=](SimpleObject object) { return value; }, arg); @@ -129,7 +129,7 @@ TEST_F(ExecutorTest, test_submitFunctionWithObjectReturnTypeObjectArgsAndVerifyE ASSERT_EQ(future.get().getValue(), value.getValue()); } -TEST_F(ExecutorTest, test_submitToFront) { +TEST_F(ExecutorTest, testTimer_submitToFront) { std::atomic ready(false); std::atomic blocked(false); std::list order; @@ -168,7 +168,7 @@ TEST_F(ExecutorTest, test_submitToFront) { ASSERT_EQ(order.back(), 2); } -TEST_F(ExecutorTest, test_executionOrderEqualToSubmitOrder) { +TEST_F(ExecutorTest, testTimer_executionOrderEqualToSubmitOrder) { WaitEvent waitSetUp; executor.submit([&waitSetUp] { waitSetUp.wait(SHORT_TIMEOUT_MS); }); @@ -218,7 +218,7 @@ struct SlowDestructor { }; /// This test verifies that the executor waits to fulfill its promise until after the task is cleaned up. -TEST_F(ExecutorTest, test_futureWaitsForTaskCleanup) { +TEST_F(ExecutorTest, testTimer_futureWaitsForTaskCleanup) { std::atomic cleanedUp(false); SlowDestructor slowDestructor; @@ -237,7 +237,7 @@ TEST_F(ExecutorTest, test_futureWaitsForTaskCleanup) { } /// This test verifies that the shutdown function completes the current task and does not accept new tasks. -TEST_F(ExecutorTest, test_shutdown) { +TEST_F(ExecutorTest, testTimer_shutdown) { std::atomic ready(false); std::atomic blocked(false); @@ -273,7 +273,7 @@ TEST_F(ExecutorTest, test_shutdown) { } /// Test that calling submit after shutdown will fail the job. -TEST_F(ExecutorTest, test_pushAfterExecutordownFail) { +TEST_F(ExecutorTest, testTimer_pushAfterExecutordownFail) { executor.shutdown(); ASSERT_TRUE(executor.isShutdown()); @@ -282,7 +282,7 @@ TEST_F(ExecutorTest, test_pushAfterExecutordownFail) { } /// Test that shutdown cancel jobs in the queue. -TEST_F(ExecutorTest, test_shutdownCancelJob) { +TEST_F(ExecutorTest, testTimer_shutdownCancelJob) { bool executed = false; WaitEvent waitSetUp, waitJobStart; std::future jobToDropResult; diff --git a/AVSCommon/Utils/test/JSONUtilTest.cpp b/AVSCommon/Utils/test/JSONUtilTest.cpp index 71b7fd43d5..562a527218 100644 --- a/AVSCommon/Utils/test/JSONUtilTest.cpp +++ b/AVSCommon/Utils/test/JSONUtilTest.cpp @@ -550,8 +550,7 @@ TEST_F(JSONUtilTest, test_retrieveArrayOfElementsWithKeySucceed) { TEST_F(JSONUtilTest, test_retrieveStringMapFromArrayOnlyKeyEntry) { std::string key{"manifest"}; std::map elements; - std::string json = - R"({"httpHeaders" : {"manifest":[{"name": "one"}]}})"; + std::string json = R"({"httpHeaders" : {"manifest":[{"name": "one"}]}})"; rapidjson::Document document; ASSERT_TRUE(jsonUtils::parseJSON(json, &document)); rapidjson::Value::ConstMemberIterator headerIterator = document.FindMember("httpHeaders"); @@ -858,8 +857,7 @@ TEST_F(JSONUtilTest, test_retrieveArrayStringMapFromArrayInvalidKey) { TEST_F(JSONUtilTest, test_retrieveArrayStringMapFromArrayEmptyArray) { std::string key{"manifest"}; std::vector> elements; - std::string json = - R"({"httpHeaders" : {"manifest":[]} })"; + std::string json = R"({"httpHeaders" : {"manifest":[]} })"; rapidjson::Document document; ASSERT_TRUE(jsonUtils::parseJSON(json, &document)); rapidjson::Value::ConstMemberIterator headerIterator = document.FindMember("httpHeaders"); @@ -875,8 +873,7 @@ TEST_F(JSONUtilTest, test_retrieveArrayStringMapFromArrayEmptyArray) { TEST_F(JSONUtilTest, test_retrieveArrayStringMapFromArrayEmptyMap) { std::string key{"manifest"}; std::vector> elements; - std::string json = - R"({"httpHeaders" : {"manifest":[ {}]}})"; + std::string json = R"({"httpHeaders" : {"manifest":[ {}]}})"; rapidjson::Document document; ASSERT_TRUE(jsonUtils::parseJSON(json, &document)); rapidjson::Value::ConstMemberIterator headerIterator = document.FindMember("httpHeaders"); diff --git a/AVSCommon/Utils/test/MIMEParserTest.cpp b/AVSCommon/Utils/test/MIMEParserTest.cpp index 21f8f5f003..30691eb9ff 100644 --- a/AVSCommon/Utils/test/MIMEParserTest.cpp +++ b/AVSCommon/Utils/test/MIMEParserTest.cpp @@ -224,6 +224,239 @@ TEST_F(MIMEParserTest, test_encodingSanity) { ASSERT_EQ(index, encodedSize); } +/* + * Helper method to run boundary check tests. + */ +void runDecodingBoundariesTest( + std::vector> partsToIndex, + std::vector> testCaseExpectedHeaders, + std::vector testCaseExpectedData, + std::string boundary, + 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); + decoder.onReceiveResponseCode(HTTPResponseCode::SUCCESS_OK); + // send the data part by part like the cloud does. + for (auto part : partsToIndex) { + status = decoder.onReceiveData(part.first.c_str(), part.first.size()); + // the class MockHTTP2MimeResponseDecodeSink is not a real gtest mock so we cannot use EXPECT_CALL + // 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); +} + +/* + * Test: Sends the boundary without a CRLF and more data, this should raise an issue as the boundary is required to be + * terminated by CRLF + * Expected Result: an ABORT error should be returned + */ +TEST_F(MIMEParserTest, test_decodingBoundariesSendBoundaryWithoutCRLF) { + /* this a structure to workaround the lack of a real mock + the indexes after each part denotes what is the expected m_index in our MockHTTP2MimeResponseDecoderSink. + in the mock the m_index attribute is incremented every time we receive a new part end. + */ + std::vector> parts = {{"--wooohooo\r\ncontent-type: multipart/related\r\n\r\n", 0}, + {"Part1", 0}, + {"\r\n--wooohoooMorePart1", 1}, + {"\r\n--wooohooo", 1}, + {"\r\ncontent-type: multipart/related\r\n\r\n", 1}, + {"Part2\r\n", 1}, + {"--wooohooo--\r\n", 1}}; + const std::vector> testCaseExpectedHeaders = { + {{"content-type", "multipart/related"}}}; + const std::vector testCaseExpectedData = {"Part1"}; + runDecodingBoundariesTest( + parts, testCaseExpectedHeaders, testCaseExpectedData, boundary, HTTP2ReceiveDataStatus::ABORT); +} + +/* + * Test: sends a -- after the boundary which will terminate the transfer considering everything else as a epilogue. + * Expected Result: consider everything before -- as data and discard everything after -- + */ +TEST_F(MIMEParserTest, test_decodingBoundariesSendBoundaryWithTerminatorShouldIgnoreEpilogue) { + /* this a structure to workaround the lack of a real mock + the indexes after each part denotes what is the expected m_index in our MockHTTP2MimeResponseDecoderSink. + in the mock the m_index attribute is incremented every time we receive a new part end. + */ + std::vector> parts = {{"--wooohooo\r\ncontent-type: multipart/related\r\n\r\n", 0}, + {"Part1", 0}, + {"\r\n--wooohooo", 1}, + {"--hello", 1}, // this will end the transfer (--wooohooo--) + {"\r\ncontent-type: multipart/related\r\n\r\n", 1}, + {"Part2\r\n", 1}, + {"--wooohooo--\r\n", 1}}; + const std::vector> testCaseExpectedHeaders = { + {{"content-type", "multipart/related"}}}; + const std::vector testCaseExpectedData = {"Part1"}; + runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, boundary); +} + +/* + * Test: sends a double boundary, the second one will be ignored + * Expected Result: second boundary ignored + */ +TEST_F(MIMEParserTest, test_decodingBoundariesSendDuplicatedBoundaryAsHeader) { + /* this a structure to workaround the lack of a real mock + the indexes after each part denotes what is the expected m_index in our MockHTTP2MimeResponseDecoderSink. + in the mock the m_index attribute is incremented every time we receive a new part end. + */ + std::vector> parts = {{"--wooohooo\r\ncontent-type: multipart/related\r\n\r\n", 0}, + {"Part1", 0}, + {"\r\n--wooohooo\r\n", 1}, + {"\r\n--wooohooo", 1}, + {"\r\ncontent-type: multipart2/related\r\n\r\n", 1}, + {"Part2\r\n", 1}, + {"--wooohooo--\r\n", 2}}; + const std::vector> testCaseExpectedHeaders = { + {{"content-type", "multipart/related"}}, {{"content-type", "multipart2/related"}}}; + const std::vector testCaseExpectedData = {"Part1", "Part2"}; + runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, boundary); +} + +/* + * Test: simulates what the AVS does when sending multipart files the boundary is usually sent alone without a trailing + * CRLF in a standalone message. Expected Result: it should be successful + */ +TEST_F(MIMEParserTest, test_decodingBoundariesAvs) { + /* this a structure to workaround the lack of a real mock + the indexes after each part denotes what is the expected m_index in our MockHTTP2MimeResponseDecoderSink. + in the mock the m_index attribute is incremented every time we receive a new part end. + */ + std::vector> parts = { + {"--wooohooo\r\ncontent-type: multipart/related\r\n\r\n", 0}, + {"1111", 0}, + {"2222", 0}, + {"3333", 0}, + {"\r\n--wooohooo", 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--wooohooo", 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, boundary); +} + +/* + * Test: sends the boundary (using the same length as the real boundary) as a data part with other data. + * Expected Result: the fake boundary should not be interpreted as such, instead it should be part of the data. + */ +TEST_F(MIMEParserTest, test_decodingBoundariesSendFakeBoundaryAsData) { + /* this a structure to workaround the lack of a real mock + the indexes after each part denotes what is the expected m_index in our MockHTTP2MimeResponseDecoderSink. + in the mock the m_index attribute is incremented every time we receive a new part end. + */ + std::vector> parts = { + {"--wooohooo\r\ncontent-type: multipart/related\r\n\r\n", 0}, + {"1111", 0}, + {"aa--wooohooo", 0}, // try to set the parser off + {"2222", 0}, + {"\r\n--wooohooo", 1}, + {"\r\ncontent-type: multipart/related\r\n\r\nlast\r\n--wooohooo", 2}, + {"--\r\n", 2}}; + const std::vector> testCaseExpectedHeaders = { + {{"content-type", "multipart/related"}}, {{"content-type", "multipart/related"}}}; + const std::vector testCaseExpectedData = {"1111aa--wooohooo2222", "last"}; + runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, boundary); +} + +/* + * Test: sends the boundary (using the same length as the real boundary) as a data part with no other data. + * Expected Result: the fake boundary should not be interpreted as such, instead it should be part of the data. + */ +TEST_F(MIMEParserTest, test_decodingBoundariesSendFakeBoundaryAsOnlyData) { + /* this a structure to workaround the lack of a real mock + the indexes after each part denotes what is the expected m_index in our MockHTTP2MimeResponseDecoderSink. + in the mock the m_index attribute is incremented every time we receive a new part end. + */ + std::vector> parts = { + {"--wooohooo\r\ncontent-type: multipart/related\r\n\r\n", 0}, + {"aa--wooohooo", 0}, // try to set the parser off + {"\r\n--wooohooo", 1}, //--boundary + {"\r\ncontent-type: multipart/related\r\n\r\nlast\r\n--wooohooo", 2}, + {"--\r\n", 2}}; + const std::vector> testCaseExpectedHeaders = { + {{"content-type", "multipart/related"}}, {{"content-type", "multipart/related"}}}; + const std::vector testCaseExpectedData = {"aa--wooohooo", "last"}; + runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, boundary); +} + +/* + * Test: sends the boundary with a trailing/ending CRLF in the same chunk instead in the chunk of the header, this is + * slightly different than what the AVS server does, it should still work as the chars are the same Expected Result: it + * should work + */ +TEST_F(MIMEParserTest, test_decodingBoundariesSendBoundaryWithCRLF) { + /* this a structure to workaround the lack of a real mock + the indexes after each part denotes what is the expected m_index in our MockHTTP2MimeResponseDecoderSink. + in the mock the m_index attribute is incremented every time we receive a new part end. + */ + std::vector> parts = { + {"--wooohooo\r\ncontent-type: multipart/related\r\n\r\n", 0}, + {"1111", 0}, + {"2222", 0}, + {"3333", 0}, + {"\r\n--wooohooo\r\n", 1}, //--boundary + {"content-type: multipart/related\r\n\r\nlast\r\n--wooohooo", 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, boundary); +} + +/* + * Test: Send the last boundary without CRLF + * Expected Result: It should work + */ +TEST_F(MIMEParserTest, test_decodingBoundariesSendEndBoundaryWithoutCRLF) { + /* this a structure to workaround the lack of a real mock + the indexes after each part denotes what is the expected m_index in our MockHTTP2MimeResponseDecoderSink. + in the mock the m_index attribute is incremented every time we receive a new part end. + */ + std::vector> parts = { + {"--wooohooo\r\ncontent-type: multipart/related\r\n\r\n", 0}, + {"1111", 0}, + {"2222", 0}, + {"3333", 0}, + {"\r\n--wooohooo", 1}, + {"\r\ncontent-type: multipart/related\r\n\r\nlast\r\n--wooohooo--", 2}}; + const std::vector> testCaseExpectedHeaders = { + {{"content-type", "multipart/related"}}, {{"content-type", "multipart/related"}}}; + const std::vector testCaseExpectedData = {"111122223333", "last"}; + runDecodingBoundariesTest(parts, testCaseExpectedHeaders, testCaseExpectedData, boundary); +} + +/* + * Test: Send the boundary without a trailing CR LF or -- + * Expected Result: It should raise an error (ABORT) because boundaries are required to have -- or CRLF after it + */ +TEST_F(MIMEParserTest, test_decodingBoundariesSendBoundaryWithData) { + /* this a structure to workaround the lack of a real mock + the indexes after each part denotes what is the expected m_index in our MockHTTP2MimeResponseDecoderSink. + in the mock the m_index attribute is incremented every time we receive a new part end. + */ + std::vector> parts = { + {"--wooohooo\r\ncontent-type: multipart/related\r\n\r\n", 0}, + {"1111", 0}, + {"\r\n--wooohooo", 1}, + {"3333", 1}, + {"\r\n--wooohooo", 1}, + {"\r\ncontent-type: multipart/related\r\n\r\nlast\r\n--wooohooo--", 1}}; + const std::vector> testCaseExpectedHeaders = { + {{"content-type", "multipart/related"}}}; + const std::vector testCaseExpectedData = {"1111"}; + runDecodingBoundariesTest( + parts, testCaseExpectedHeaders, testCaseExpectedData, boundary, HTTP2ReceiveDataStatus::ABORT); +} + TEST_F(MIMEParserTest, test_decodingSanity) { /// We choose an arbitrary buffer size const int bufferSize{25}; diff --git a/AVSCommon/Utils/test/UUIDGenerationTest.cpp b/AVSCommon/Utils/test/UUIDGenerationTest.cpp index d590923505..9d7371d7be 100644 --- a/AVSCommon/Utils/test/UUIDGenerationTest.cpp +++ b/AVSCommon/Utils/test/UUIDGenerationTest.cpp @@ -111,25 +111,24 @@ TEST_F(UUIDGenerationTest, test_uUIDVariant) { } /** - * Call @c setSalt without error, with various inputs + * Call @c addSeed without error, with various inputs */ -TEST_F(UUIDGenerationTest, setSaltTest) { - setSalt(""); +TEST_F(UUIDGenerationTest, addSeedTest) { + std::vector seeds; + addSeeds(seeds); auto uuid = generateUUID(); ASSERT_EQ(UUID_VERSION, uuid.substr(UUID_VERSION_OFFSET, 1)); ASSERT_EQ(UUID_LENGTH, uuid.length()); - setSalt("G2A14Q02920602PT"); - uuid = generateUUID(); - ASSERT_EQ(UUID_VERSION, uuid.substr(UUID_VERSION_OFFSET, 1)); - ASSERT_EQ(UUID_LENGTH, uuid.length()); - setSalt("123456789"); + seeds.push_back(0xafd0f00a); + seeds.push_back(0xbf32d31f); + addSeeds(seeds); uuid = generateUUID(); ASSERT_EQ(UUID_VERSION, uuid.substr(UUID_VERSION_OFFSET, 1)); ASSERT_EQ(UUID_LENGTH, uuid.length()); } /** - * Call @c generateUUID and setSalt from multiple threads and check that all are able to complete + * Call @c generateUUID and addSeed from multiple threads and check that all are able to complete * successfully Check for uniqueness of the UUIDs generated. */ TEST_F(UUIDGenerationTest, test_multipleConcurrentSaltSettings) { @@ -147,7 +146,9 @@ TEST_F(UUIDGenerationTest, test_multipleConcurrentSaltSettings) { auto future_seed = std::async( std::launch::async, [](int i) { - setSalt(std::to_string(i)); + std::vector vec; + vec.push_back(i); + addSeeds(vec); return generateUUID(); }, i); diff --git a/AVSGatewayManager/include/AVSGatewayManager/AVSGatewayManager.h b/AVSGatewayManager/include/AVSGatewayManager/AVSGatewayManager.h index 49a4e5add2..9a582bd6bc 100644 --- a/AVSGatewayManager/include/AVSGatewayManager/AVSGatewayManager.h +++ b/AVSGatewayManager/include/AVSGatewayManager/AVSGatewayManager.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -46,9 +47,27 @@ class AVSGatewayManager , public avsCommon::sdkInterfaces::PostConnectOperationProviderInterface , public registrationManager::CustomerDataHandler { public: + /** + * Creates an instance of the @c AVSGatewayManagerInterface. + * + * @param avsGatewayManagerStorage The @c AVSGatewayManagerInterface to store avs gateway information. + * @param customerDataManager The @c CustomerDataManager object that will track the CustomerDataHandler. + * @param configurationRoot The @c ConfigurationNode to get AVS gateway information from the config file. + * @param providerRegistrar Object with which to register the new instance as a post connect operation provider. + * @return A new instance of the @c AVSGatewayManager. + */ + static std::shared_ptr createAVSGatewayManagerInterface( + std::unique_ptr avsGatewayManagerStorage, + const std::shared_ptr& customerDataManager, + const std::shared_ptr& configurationRoot, + const std::shared_ptr< + acsdkPostConnectOperationProviderRegistrarInterfaces::PostConnectOperationProviderRegistrarInterface>& + providerRegistrar); + /** * Creates an instance of the @c AVSGatewayManager. * + * @deprecated * @param avsGatewayManagerStorage The @c AVSGatewayManagerInterface to store avs gateway information. * @param customerDataManager The @c CustomerDataManager object that will track the CustomerDataHandler. * @param configurationRoot The @c ConfigurationNode to get AVS gateway information from the config file. diff --git a/AVSGatewayManager/include/AVSGatewayManager/Storage/AVSGatewayManagerStorage.h b/AVSGatewayManager/include/AVSGatewayManager/Storage/AVSGatewayManagerStorage.h index a324c8c55a..56185112b6 100644 --- a/AVSGatewayManager/include/AVSGatewayManager/Storage/AVSGatewayManagerStorage.h +++ b/AVSGatewayManager/include/AVSGatewayManager/Storage/AVSGatewayManagerStorage.h @@ -31,6 +31,15 @@ namespace storage { */ class AVSGatewayManagerStorage : public AVSGatewayManagerStorageInterface { public: + /** + * Creates an instance of @ AVSGatewayManagerStorageInterface. + * + * @param miscStorage The underlying miscellaneous storage to store gateway verification data. + * @return A unique pointer to the instance of the newly created @c AVSGatewayManagerStorage. + */ + static std::unique_ptr createAVSGatewayManagerStorageInterface( + const std::shared_ptr& miscStorage); + /** * Creates an instance of the @c AVSGatewayManagerStorage. * diff --git a/AVSGatewayManager/src/AVSGatewayManager.cpp b/AVSGatewayManager/src/AVSGatewayManager.cpp index 1660927ae0..5aae0fe6a1 100644 --- a/AVSGatewayManager/src/AVSGatewayManager.cpp +++ b/AVSGatewayManager/src/AVSGatewayManager.cpp @@ -45,6 +45,34 @@ static const std::string AVS_GATEWAY = "avsGateway"; /// Default @c AVS gateway to connect to. static const std::string DEFAULT_AVS_GATEWAY = "https://alexa.na.gateway.devices.a2z.com"; +std::shared_ptr AVSGatewayManager:: + createAVSGatewayManagerInterface( + std::unique_ptr avsGatewayManagerStorage, + const std::shared_ptr& customerDataManager, + const std::shared_ptr& configurationRoot, + const std::shared_ptr< + acsdkPostConnectOperationProviderRegistrarInterfaces::PostConnectOperationProviderRegistrarInterface>& + providerRegistrar) { + if (!configurationRoot) { + ACSDK_ERROR(LX("createAVSGatewayManagerInterfaceFailed").d("reason", "nullConfigurationRoot")); + return nullptr; + } + if (!providerRegistrar) { + ACSDK_ERROR(LX("createAVSGatewayManagerInterfaceFailed").d("reason", "nullProviderRegistrar")); + return nullptr; + } + auto gatewayManager = create(std::move(avsGatewayManagerStorage), customerDataManager, *configurationRoot); + if (!gatewayManager) { + ACSDK_ERROR(LX("createAVSGatewayManagerInterfaceFailed").d("reason", "createFailed")); + return nullptr; + } + if (!providerRegistrar->registerProvider(gatewayManager)) { + ACSDK_ERROR(LX("createAVSGatewayManagerInterfaceFailed").d("reason", "registerProviderFailed")); + return nullptr; + } + return gatewayManager; +} + std::shared_ptr AVSGatewayManager::create( std::shared_ptr avsGatewayManagerStorage, std::shared_ptr customerDataManager, diff --git a/AVSGatewayManager/src/CMakeLists.txt b/AVSGatewayManager/src/CMakeLists.txt index 458ab686f7..9a4ca9d640 100644 --- a/AVSGatewayManager/src/CMakeLists.txt +++ b/AVSGatewayManager/src/CMakeLists.txt @@ -12,7 +12,10 @@ target_include_directories(AVSGatewayManager PUBLIC "$" "$") -target_link_libraries(AVSGatewayManager AVSCommon RegistrationManager) +target_link_libraries(AVSGatewayManager + acsdkPostConnectOperationProviderRegistrarInterfaces + AVSCommon + RegistrationManager) # install target asdk_install() diff --git a/AVSGatewayManager/src/Storage/AVSGatewayManagerStorage.cpp b/AVSGatewayManager/src/Storage/AVSGatewayManagerStorage.cpp index 422f2189da..e1331e3527 100644 --- a/AVSGatewayManager/src/Storage/AVSGatewayManagerStorage.cpp +++ b/AVSGatewayManager/src/Storage/AVSGatewayManagerStorage.cpp @@ -51,6 +51,11 @@ static const std::string GATEWAY_URL_KEY = "gatewayURL"; /// Json key for isGatewayVerified. static const std::string IS_VERIFIED_KEY = "isVerified"; +std::unique_ptr AVSGatewayManagerStorage::createAVSGatewayManagerStorageInterface( + const std::shared_ptr& miscStorage) { + return create(miscStorage); +} + std::unique_ptr AVSGatewayManagerStorage::create( std::shared_ptr miscStorage) { if (!miscStorage) { @@ -158,12 +163,6 @@ void AVSGatewayManagerStorage::clear() { .d("table", VERIFICATION_STATE_TABLE) .d("component", COMPONENT_NAME) .m("Please clear the table for proper future functioning.")); - } else if (!m_miscStorage->deleteTable(COMPONENT_NAME, VERIFICATION_STATE_TABLE)) { - ACSDK_ERROR(LX("clearFailed") - .d("reason", "Unable to delete the table") - .d("table", VERIFICATION_STATE_TABLE) - .d("component", COMPONENT_NAME) - .m("Please delete the table for proper future functioning.")); } } } else { diff --git a/ApplicationUtilities/AndroidUtilities/include/AndroidUtilities/PlatformSpecificValues.h b/ApplicationUtilities/AndroidUtilities/include/AndroidUtilities/PlatformSpecificValues.h new file mode 100644 index 0000000000..1538ed67bf --- /dev/null +++ b/ApplicationUtilities/AndroidUtilities/include/AndroidUtilities/PlatformSpecificValues.h @@ -0,0 +1,35 @@ +/* + * 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_APPLICATIONUTILITIES_ANDROIDUTILITIES_INCLUDE_ANDROIDUTILITIES_PLATFORMSPECIFICVALUES_H_ +#define ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_ANDROIDUTILITIES_INCLUDE_ANDROIDUTILITIES_PLATFORMSPECIFICVALUES_H_ + +#include + +#include "AndroidUtilities/AndroidSLESEngine.h" + +namespace alexaClientSDK { +namespace applicationUtilities { +namespace androidUtilities { + +struct PlatformSpecificValues { + std::shared_ptr openSlEngine; +}; + +} // namespace androidUtilities +} // namespace applicationUtilities +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_ANDROIDUTILITIES_INCLUDE_ANDROIDUTILITIES_PLATFORMSPECIFICVALUES_H_ diff --git a/ApplicationUtilities/AndroidUtilities/src/AndroidSLESMicrophone.cpp b/ApplicationUtilities/AndroidUtilities/src/AndroidSLESMicrophone.cpp index e534e457bf..f4a263cd5c 100644 --- a/ApplicationUtilities/AndroidUtilities/src/AndroidSLESMicrophone.cpp +++ b/ApplicationUtilities/AndroidUtilities/src/AndroidSLESMicrophone.cpp @@ -151,6 +151,9 @@ bool AndroidSLESMicrophone::startStreamingMicrophoneData() { bool AndroidSLESMicrophone::stopStreamingMicrophoneData() { ACSDK_INFO(LX(__func__)); std::lock_guard lock{m_mutex}; + if (!m_isStreaming) { + return true; + } if (m_recorderObject && m_recorderInterface) { auto result = (*m_recorderInterface)->SetRecordState(m_recorderInterface, SL_RECORDSTATE_STOPPED); diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h index 72523b2cbd..9b149bfd79 100644 --- a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h +++ b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClient.h @@ -16,9 +16,9 @@ #ifndef ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_DEFAULTCLIENT_H_ #define ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_DEFAULTCLIENT_H_ -#include #include #include +#include #include #include #include @@ -28,19 +28,19 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include +#include #include #include -#include -#include -#include #include #include #include @@ -49,9 +49,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -71,21 +73,25 @@ #include #include #include +#include #include #include #include #include -#include #include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#ifdef ENABLE_CAPTIONS -#include -#endif - #ifdef ENABLE_PCC #include #include @@ -130,10 +136,156 @@ namespace defaultClient { */ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerInterface { public: + using DefaultClientManufactory = acsdkManufactory::Manufactory< + std::shared_ptr, + std::shared_ptr, + std::shared_ptr, + std::shared_ptr, /// 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, + 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, + 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>; + + /** + * Creates and initializes a default AVS SDK client. To connect the client to AVS, users should make a call to + * connect() after creation. + * + * @param manufactory @c Manufactory for creating various instances used by DefaultlClient. + * @param alertsMediaPlayer The media player to use to play alerts from. + * @param bluetoothMediaPlayer The media player to play bluetooth content. + * @param ringtoneMediaPlayer The media player to play Comms ringtones. + * @param systemSoundMediaPlayer The media player to play system sounds. + * @param alertsSpeaker The speaker to control volume of alerts. + * @param bluetoothSpeaker The speaker to control volume of bluetooth. + * @param ringtoneSpeaker The speaker to control volume of Comms ringtones. + * @param systemSoundSpeaker The speaker to control volume of system sounds. + * @param additionalSpeakers A map of additional speakers to receive volume changes. +#ifdef ENABLE_PCC + * @param phoneSpeaker Interface to speaker for phone calls. + * @param phoneCaller Interface for making phone calls. +#endif +#ifdef ENABLE_MCC + * @param meetingSpeaker Interface to speaker for meetings. + * @param meetingClient Meeting client for meeting client controller. + * @param calendarClient Calendar client for meeting client controller. +#endif +#ifdef ENABLE_COMMS_AUDIO_PROXY + * @param commsMediaPlayer The media player to play Comms calling audio. + * @param commsSpeaker The speaker to control volume of Comms calling audio. + * @param sharedDataStream The stream to use which has the audio from microphone. +#endif + * @param audioFactory The audioFactory is a component that provides unique audio streams. + * @param alertStorage The storage interface that will be used to store alerts. + * @param notificationsStorage The storage interface that will be used to store notification indicators. + * @param deviceSettingStorage The storage interface that will be used to store device settings. + * @param bluetoothStorage The storage interface that will be used to store bluetooth data. + * @param alexaDialogStateObservers Observers that can be used to be notified of Alexa dialog related UX state + * changes. + * @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 systemTimezone Optional object used to set the system timezone. + * @param firmwareVersion The firmware version to report to @c AVS or @c INVALID_FIRMWARE_VERSION. + * @param sendSoftwareInfoOnConnected Whether to send SoftwareInfo upon connecting to @c AVS. + * @param softwareInfoSenderObserver Object to receive notifications about sending SoftwareInfo. + * @param bluetoothDeviceManager The @c BluetoothDeviceManager instance used to create the Bluetooth CA. + * @param diagnostics Diagnostics interface which provides suite of APIs for diagnostic insight into SDK. + * @param externalCapabilitiesBuilder Optional object used to build capabilities that are not included in the SDK. + * @param startAlertSchedulingOnInitialization Whether to start scheduling alerts after client initialization. If + * this is set to false, no alert scheduling will occur until onSystemClockSynchronized is called. + * @param firstInteractionAudioProvider Optional object used in the first interaction started from + * the alexa voice service + * @return A @c std::unique_ptr to a DefaultClient if all went well or @c nullptr otherwise. + */ + static std::unique_ptr create( + const std::shared_ptr& manufactory, + std::shared_ptr alertsMediaPlayer, + std::shared_ptr bluetoothMediaPlayer, + std::shared_ptr ringtoneMediaPlayer, + std::shared_ptr systemSoundMediaPlayer, + std::shared_ptr alertsSpeaker, + std::shared_ptr bluetoothSpeaker, + std::shared_ptr ringtoneSpeaker, + std::shared_ptr systemSoundSpeaker, + const std::multimap< + avsCommon::sdkInterfaces::ChannelVolumeInterface::Type, + std::shared_ptr> additionalSpeakers, +#ifdef ENABLE_PCC + std::shared_ptr phoneSpeaker, + std::shared_ptr phoneCaller, +#endif +#ifdef ENABLE_MCC + std::shared_ptr meetingSpeaker, + std::shared_ptr meetingClient, + std::shared_ptr calendarClient, +#endif +#ifdef ENABLE_COMMS_AUDIO_PROXY + std::shared_ptr commsMediaPlayer, + std::shared_ptr commsSpeaker, + std::shared_ptr sharedDataStream, +#endif + std::shared_ptr audioFactory, + std::shared_ptr alertStorage, + std::shared_ptr notificationsStorage, + std::unique_ptr deviceSettingStorage, + std::shared_ptr bluetoothStorage, + std::unordered_set> + alexaDialogStateObservers, + std::unordered_set> + connectionObservers, + bool isGuiSupported, + std::unordered_set> + enabledConnectionRules = std::unordered_set< + std::shared_ptr>(), + std::shared_ptr systemTimezone = nullptr, + avsCommon::sdkInterfaces::softwareInfo::FirmwareVersion firmwareVersion = + avsCommon::sdkInterfaces::softwareInfo::INVALID_FIRMWARE_VERSION, + bool sendSoftwareInfoOnConnected = false, + std::shared_ptr softwareInfoSenderObserver = + nullptr, + std::unique_ptr bluetoothDeviceManager = + nullptr, + std::shared_ptr diagnostics = nullptr, + const std::shared_ptr& externalCapabilitiesBuilder = nullptr, + bool startAlertSchedulingOnInitialization = true, + capabilityAgents::aip::AudioProvider firstInteractionAudioProvider = + capabilityAgents::aip::AudioProvider::null()); + /** * Creates and initializes a default AVS SDK client. To connect the client to AVS, users should make a call to * connect() after creation. * + * @deprecated * @param deviceInfo DeviceInfo which reflects the device setup credentials. * @param customerDataManager CustomerDataManager instance to be used by RegistrationManager and instances of * all classes extending CustomDataHandler. @@ -198,9 +350,12 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI * @param startAlertSchedulingOnInitialization Whether to start scheduling alerts after client initialization. If * this is set to false, no alert scheduling will occur until onSystemClockSynchronized is called. * @param messageRouterFactory Object used to instantiate @c MessageRouter in the SDK. + * @param expectSpeechTimeoutHandler An optional object that applications may provide to specify external handling +of the @c ExpectSpeech directive's timeout. If provided, this function must remain valid for the lifetime of the @c +AudioInputProcessor. + * @param firstInteractionAudioProvider Optional object used in the first interaction started from + * the alexa voice service * @return A @c std::unique_ptr to a DefaultClient if all went well or @c nullptr otherwise. - * - * TODO: Allow the user to pass in a MediaPlayer factory rather than each media player individually. */ static std::unique_ptr create( std::shared_ptr deviceInfo, @@ -209,7 +364,7 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI externalMusicProviderMediaPlayers, const std::unordered_map>& externalMusicProviderSpeakers, - const capabilityAgents::externalMediaPlayer::ExternalMediaPlayer::AdapterCreationMap& adapterCreationMap, + const acsdkExternalMediaPlayer::ExternalMediaPlayer::AdapterCreationMap& adapterCreationMap, std::shared_ptr speakMediaPlayer, std::unique_ptr audioMediaPlayerFactory, std::shared_ptr alertsMediaPlayer, @@ -280,7 +435,12 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::make_shared(), bool startAlertSchedulingOnInitialization = true, std::shared_ptr messageRouterFactory = - std::make_shared()); + std::make_shared(), + const std::shared_ptr& + expectSpeechTimeoutHandler = nullptr, + capabilityAgents::aip::AudioProvider firstInteractionAudioProvider = + capabilityAgents::aip::AudioProvider::null()); + /** * Connects the client to AVS. After this call, users can observe the state of the connection asynchronously by * using a connectionObserver that was passed in to the create() function. @@ -460,7 +620,7 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI * @param observer The observer to add. */ void addExternalMediaPlayerObserver( - std::shared_ptr observer); + std::shared_ptr observer); /** * Removes an observer to be notified of ExternalMediaPlayer changes. @@ -468,7 +628,7 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI * @param observer The observer to remove. */ void removeExternalMediaPlayerObserver( - std::shared_ptr observer); + std::shared_ptr observer); /** * Adds an observer to be notified of bluetooth device changes. @@ -489,6 +649,8 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI /** * Adds a presenter responsible for displaying formatted captions content. * + * @deprecated Applications should use the manufactory to create their CaptionPresenterInterface and + * inject it with the CaptionManagerInterface. See the SampleApplicationComponent for an example. * @param presenter The @c CaptionPresenterInterface to add. */ void addCaptionPresenter(std::shared_ptr presenter); @@ -496,6 +658,9 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI /** * Sets the media players that can produce or control captioned content. * + * @deprecated Applications should use an ApplicationAudioPipelineFactoryInterface to instantiate media players + * and register them with the CaptionManager when they transition to the manufactory (e.g. see + * GstreamerAudioPipelineFactory). * @param mediaPlayers The @c MediaPlayerInterface instances to add. */ void setCaptionMediaPlayers( @@ -576,7 +741,7 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI * reference to the @c DefaultClient. * @return shared_ptr to the EqualizerController. */ - std::shared_ptr getEqualizerController(); + std::shared_ptr getEqualizerController(); /** * Adds a EqualizerControllerListener to be notified of Equalizer state changes. @@ -584,7 +749,7 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI * @param listener The listener to be notified of Equalizer state changes. */ void addEqualizerControllerListener( - std::shared_ptr listener); + std::shared_ptr listener); /** * Removes a EqualizerControllerListener to be notified of Equalizer state changes. @@ -592,7 +757,7 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI * @param listener The listener to no longer be notified of Equalizer state changes. */ void removeEqualizerControllerListener( - std::shared_ptr listener); + std::shared_ptr listener); /** * Adds a ContextManagerObserver to be notified of Context state changes. @@ -634,7 +799,9 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::future notifyOfHoldToTalkStart( capabilityAgents::aip::AudioProvider holdToTalkAudioProvider, - std::chrono::steady_clock::time_point startOfSpeechTimestamp = std::chrono::steady_clock::now()) override; + std::chrono::steady_clock::time_point startOfSpeechTimestamp = std::chrono::steady_clock::now(), + avsCommon::avs::AudioInputStream::Index beginIndex = + capabilityAgents::aip::AudioInputProcessor::INVALID_INDEX) override; std::future notifyOfHoldToTalkEnd() override; @@ -655,7 +822,7 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI * * @return An endpoint builder. */ - std::unique_ptr createEndpointBuilder(); + std::shared_ptr createEndpointBuilder(); /** * Registers an endpoint with the @c EndpointRegistrationManagerInterface. @@ -686,6 +853,22 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI */ std::shared_ptr getDefaultEndpointBuilder(); + /** + * Adds an @c AudioInputProcessorObserver to be alerted on @c AudioInputProcessor related state changes. + * + * @param observer The observer to be notified upon @c AudioInputProcessor related state changes. + */ + void addAudioInputProcessorObserver( + const std::shared_ptr& observer); + + /** + * Removes an @c AudioInputProcessorObserver to be alerted on @c AudioInputProcessor related state changes. + * + * @param observer The observer to be removed as an @c AudioInputProcessor observer. + */ + void removeAudioInputProcessorObserver( + const std::shared_ptr& observer); + /** * Adds an observer to be notified when the call state has changed. * @@ -752,64 +935,52 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI void onSystemClockSynchronized(); /** - * Destructor. + * Register an ExternalMediaPlayerAdapterHandler with the ExternalMediaPlayer CA + * Multiple adapter handlers can be added to the ExternalMediaPlayer by repeatedly calling this function. + * + * @param externalMediaPlayerAdapterHandler Pointer to the EMPAdapterHandler to register */ - ~DefaultClient(); + void registerExternalMediaPlayerAdapterHandler( + std::shared_ptr + externalMediaPlayerAdapterHandler); -private: /** - * Constructor. + * Gets the @c ShutdownManagerInterface for when it is time to shut down the SDK. * - * @param deviceInfo DeviceInfo which reflects the device setup credentials. + * This method is required to support legacy applications that have not transitioned to fully integrating + * the manufactory. + * @return A shared_ptr to the @c ShutdownManagerInterface. */ - DefaultClient(const avsCommon::utils::DeviceInfo& deviceInfo); + std::shared_ptr getShutdownManager(); + /** + * Destructor. + */ + ~DefaultClient(); + +private: /** * Initializes the SDK and "glues" all the components together. * - * @param customerDataManager CustomerDataManager instance to be used by RegistrationManager and instances of - * all classes extending CustomDataHandler. - * @param externalMusicProviderMediaPlayers The map of to use to play content from each - * external music provider. - * @param externalMusicProviderSpeakers The map of to use to track volume of each - * external music provider media player. - * @param adapterCreationMap The map of to use when creating the adapters for the - * different music providers supported by ExternalMediaPlayer. - * @param speakMediaPlayer The media player to use to play Alexa speech from. - * @param audioMediaPlayerFactory The media player to use to generate players for Alexa audio content. + * @param manufactory @c Manufactory for creating various instances used by DefaultlClient. * @param alertsMediaPlayer The media player to use to play alerts from. - * @param notificationsMediaPlayer The media player to play notification indicators. * @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 speakSpeaker The speaker to control volume of Alexa speech. - * @param audioSpeakers A list of speakers to control volume of Alexa audio content. * @param alertsSpeaker The speaker to control volume of alerts. - * @param notificationsSpeaker The speaker to control volume of notifications. * @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 equalizerRuntimeSetup Equalizer component runtime setup * @param audioFactory The audioFactory is a component the provides unique audio streams. - * @param authDelegate The component that provides the client with valid LWA authorization. * @param alertStorage The storage interface that will be used to store alerts. - * @param messageStorage The storage interface that will be used to store certified sender messages. * @param notificationsStorage The storage interface that will be used to store notification indicators. * @param deviceSettingsStorage The storage interface that will be used to store device settings. * @param bluetoothStorage The storage interface that will be used to store bluetooth data. - * @param miscStorage The storage interface that will be used to store key / value pairs. * @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 capabilitiesDelegate The component that provides the client with the ability to send messages to the - * Capabilities API. - * @param contextManager The @c ContextManager which will provide the context for various components. - * @param transportFactory The object passed in here will be used whenever a new transport object - * for AVS communication is needed. - * @param avsGatewayManager The @c AVSGatewayManager instance used to create the ApiGateway CA. - * @param localeAssetsManager The device locale assets manager. * @param enabledConnectionRules The set of @c BluetoothDeviceConnectionRuleInterface instances used to * create the Bluetooth CA. * @param systemTimezone Optional object used to set the system timezone. @@ -817,34 +988,22 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI * @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 metricRecorder The optional metric recorder object used to capture metrics. - * @param powerResourceManager Object to manage power resource. * @param diagnostics Diagnostics interface that provides suite of APIs for insights into SDK. * @param externalCapabilitiesBuilder Object used to build capabilities that are not included in the SDK. * @param channelVolumeFactory Optional object used to build @c ChannelVolumeInterface in the SDK. * @param startAlertSchedulingOnInitialization Whether to start scheduling alerts after client initialization. If * this is set to false, no alert scheduling will occur until onSystemClockSynchronized is called. - * @param messageRouterFactory Object used to instantiate @c MessageRouter in the SDK. + * @param firstInteractionAudioProvider Optional object used in the first interaction started from + * the alexa voice service * @return Whether the SDK was initialized properly. */ bool initialize( - std::shared_ptr customerDataManager, - const std::unordered_map>& - externalMusicProviderMediaPlayers, - const std::unordered_map>& - externalMusicProviderSpeakers, - const capabilityAgents::externalMediaPlayer::ExternalMediaPlayer::AdapterCreationMap& adapterCreationMap, - std::shared_ptr speakMediaPlayer, - std::unique_ptr audioMediaPlayerFactory, + const std::shared_ptr& manufactory, std::shared_ptr alertsMediaPlayer, - std::shared_ptr notificationsMediaPlayer, std::shared_ptr bluetoothMediaPlayer, std::shared_ptr ringtoneMediaPlayer, std::shared_ptr systemSoundMediaPlayer, - std::shared_ptr speakSpeaker, - std::vector> audioSpeakers, std::shared_ptr alertsSpeaker, - std::shared_ptr notificationsSpeaker, std::shared_ptr bluetoothSpeaker, std::shared_ptr ringtoneSpeaker, std::shared_ptr systemSoundSpeaker, @@ -865,26 +1024,16 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr commsSpeaker, std::shared_ptr sharedDataStream, #endif - std::shared_ptr equalizerRuntimeSetup, std::shared_ptr audioFactory, - std::shared_ptr authDelegate, std::shared_ptr alertStorage, - std::shared_ptr messageStorage, std::shared_ptr notificationsStorage, std::shared_ptr deviceSettingStorage, std::shared_ptr bluetoothStorage, - std::shared_ptr miscStorage, std::unordered_set> alexaDialogStateObservers, std::unordered_set> connectionObservers, - std::shared_ptr internetConnectionMonitor, bool isGuiSupported, - std::shared_ptr capabilitiesDelegate, - std::shared_ptr contextManager, - std::shared_ptr transportFactory, - std::shared_ptr avsGatewayManager, - std::shared_ptr localeAssetsManager, std::unordered_set> enabledConnectionRules, std::shared_ptr systemTimezone, @@ -892,26 +1041,20 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI bool sendSoftwareInfoOnConnected, std::shared_ptr softwareInfoSenderObserver, std::unique_ptr bluetoothDeviceManager, - std::shared_ptr metricRecorder, - std::shared_ptr powerResourceManager, std::shared_ptr diagnostics, const std::shared_ptr& externalCapabilitiesBuilder, - std::shared_ptr channelVolumeFactory, bool startAlertSchedulingOnInitialization, - std::shared_ptr messageRouterFactory); + capabilityAgents::aip::AudioProvider firstInteractionAudioProvider); /// The directive sequencer. std::shared_ptr m_directiveSequencer; /// The focus manager for audio channels. - std::shared_ptr m_audioFocusManager; + std::shared_ptr m_audioFocusManager; /// The focus manager for visual channels. std::shared_ptr m_visualFocusManager; - /// The audio activity tracker. - std::shared_ptr m_audioActivityTracker; - /// The visual activity tracker. std::shared_ptr m_visualActivityTracker; @@ -919,17 +1062,15 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr m_messageRouter; /// The connection manager. - std::shared_ptr m_connectionManager; + std::shared_ptr m_connectionManager; std::shared_ptr m_internetConnectionMonitor; -#ifdef ENABLE_CAPTIONS /// The captions manager. - std::shared_ptr m_captionManager; -#endif + std::shared_ptr m_captionManager; /// The exception sender. - std::shared_ptr m_exceptionSender; + std::shared_ptr m_exceptionSender; /// The certified sender. std::shared_ptr m_certifiedSender; @@ -941,17 +1082,14 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr m_speechSynthesizer; /// The audio player. - std::shared_ptr m_audioPlayer; + std::shared_ptr m_audioPlayer; /// The external media player. - std::shared_ptr m_externalMediaPlayer; + std::shared_ptr m_externalMediaPlayer; /// The alexa interface message sender. std::shared_ptr m_alexaMessageSender; - /// The alexa interface capability agent. - std::shared_ptr m_alexaCapabilityAgent; - /// The api gateway capability agent. std::shared_ptr m_apiGatewayCapabilityAgent; @@ -991,13 +1129,10 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr m_dialogUXStateAggregator; /// The playbackRouter. - std::shared_ptr m_playbackRouter; - - /// The playbackController capability agent. - std::shared_ptr m_playbackController; + std::shared_ptr m_playbackRouter; /// The speakerManager. Used for controlling the volume and mute settings of @c SpeakerInterface objects. - std::shared_ptr m_speakerManager; + std::shared_ptr m_speakerManager; /// The TemplateRuntime capability agent. std::shared_ptr m_templateRuntime; @@ -1006,13 +1141,13 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr m_dndCapabilityAgent; /// The Equalizer capability agent. - std::shared_ptr m_equalizerCapabilityAgent; + std::shared_ptr m_equalizerCapabilityAgent; /// The @c EqualizerController instance. - std::shared_ptr m_equalizerController; + std::shared_ptr m_equalizerController; /// Equalizer runtime setup to be used in the SDK. - std::shared_ptr m_equalizerRuntimeSetup; + std::shared_ptr m_equalizerRuntimeSetup; /// Mutex to serialize access to m_softwareInfoSender. std::mutex m_softwareInfoSenderMutex; @@ -1025,6 +1160,9 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr m_revokeAuthorizationHandler; #endif + /// The @c AuthDelegateInterface used for authorization events. + std::shared_ptr m_authDelegate; + /// The RegistrationManager used to control customer registration. std::shared_ptr m_registrationManager; @@ -1038,7 +1176,7 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr m_deviceSettingStorage; /// DeviceInfo which reflects the device setup credentials. - avsCommon::utils::DeviceInfo m_deviceInfo; + std::shared_ptr m_deviceInfo; /// The device context manager. std::shared_ptr m_contextManager; @@ -1047,7 +1185,7 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI std::shared_ptr m_endpointRegistrationManager; /// The endpoint builder for the default endpoint with AVS Capabilities. - std::shared_ptr m_defaultEndpointBuilder; + std::shared_ptr m_defaultEndpointBuilder; /// The @c AVSGatewayManager instance used in the AVS Gateway connection sequence. std::shared_ptr m_avsGatewayManager; @@ -1074,6 +1212,9 @@ class DefaultClient : public avsCommon::sdkInterfaces::SpeechInteractionHandlerI /// The SoftwareComponentReporter Capability Agent. std::shared_ptr m_softwareReporterCapabilityAgent; + + /// The @c ShutdownManagerInterface for shutting down the SDK. + std::shared_ptr m_shutdownManager; }; } // namespace defaultClient diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h new file mode 100644 index 0000000000..f16f44788a --- /dev/null +++ b/ApplicationUtilities/DefaultClient/include/DefaultClient/DefaultClientComponent.h @@ -0,0 +1,135 @@ +/* + * 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_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_DEFAULTCLIENTCOMPONENT_H_ +#define ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_DEFAULTCLIENTCOMPONENT_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DefaultClient/EqualizerRuntimeSetup.h" +#include "DefaultClient/StubApplicationAudioPipelineFactory.h" + +namespace alexaClientSDK { +namespace defaultClient { + +/** + * Definition of a Manufactory component for the Default Client. + * + * This component provides backwards compatibility for applications that use the Default Client's + * non-manufactory method of initialization. + */ +using DefaultClientComponent = acsdkManufactory::Component< + 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, + 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, + 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>; + +/** + * Get the manufactory @c Component for (legacy) @c DefaultClient initialization. + * + * @return The manufactory @c Component for (legacy) @c DefaultClient initialization. + */ +DefaultClientComponent getComponent( + const std::shared_ptr& authDelegate, + const std::shared_ptr& contextManager, + const std::shared_ptr& localeAssetsManager, + const std::shared_ptr& deviceInfo, + const std::shared_ptr& customerDataManager, + const std::shared_ptr& miscStorage, + const std::shared_ptr& internetConnectionMonitor, + const std::shared_ptr& avsGatewayManager, + const std::shared_ptr& capabilitiesDelegate, + const std::shared_ptr& metricRecorder, + const std::shared_ptr& diagnostics, + const std::shared_ptr& transportFactory, + const std::shared_ptr& messageRouterFactory, + const std::shared_ptr& channelVolumeFactory, + const std::shared_ptr& expectSpeechTimeoutHandler, + const std::shared_ptr& equalizerRuntimeSetup, + const std::shared_ptr& stubAudioPipelineFactory, + const std::shared_ptr& + audioMediaResourceProvider, + const std::shared_ptr& messageStorage, + const std::shared_ptr& powerResourceManager, + const acsdkExternalMediaPlayer::ExternalMediaPlayer::AdapterCreationMap& adapterCreationMap); + +} // namespace defaultClient +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_DEFAULTCLIENTCOMPONENT_H_ diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/DeviceSettingsManagerBuilder.h b/ApplicationUtilities/DefaultClient/include/DefaultClient/DeviceSettingsManagerBuilder.h index 00ef015598..d3445c793d 100644 --- a/ApplicationUtilities/DefaultClient/include/DefaultClient/DeviceSettingsManagerBuilder.h +++ b/ApplicationUtilities/DefaultClient/include/DefaultClient/DeviceSettingsManagerBuilder.h @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -56,7 +56,7 @@ class DeviceSettingsManagerBuilder : public settings::SettingsManagerBuilderBase DeviceSettingsManagerBuilder( std::shared_ptr settingStorage, std::shared_ptr messageSender, - std::shared_ptr connectionManager, + std::shared_ptr connectionManager, std::shared_ptr dataManager); /** @@ -177,7 +177,7 @@ class DeviceSettingsManagerBuilder : public settings::SettingsManagerBuilderBase std::shared_ptr m_messageSender; /// The connection manager that manages the connection with AVS. - std::shared_ptr m_connectionManager; + std::shared_ptr m_connectionManager; /// The dataManager object that will track the CustomerDataHandler. std::shared_ptr m_dataManager; diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/EqualizerRuntimeSetup.h b/ApplicationUtilities/DefaultClient/include/DefaultClient/EqualizerRuntimeSetup.h index 48e0d6d59e..ea2fab1186 100644 --- a/ApplicationUtilities/DefaultClient/include/DefaultClient/EqualizerRuntimeSetup.h +++ b/ApplicationUtilities/DefaultClient/include/DefaultClient/EqualizerRuntimeSetup.h @@ -16,11 +16,12 @@ #ifndef ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_EQUALIZERRUNTIMESETUP_H_ #define ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_EQUALIZERRUNTIMESETUP_H_ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include @@ -31,103 +32,90 @@ namespace defaultClient { /** * Class containing references to implementations for all equalizer related interfaces. */ -class EqualizerRuntimeSetup { +class EqualizerRuntimeSetup : public acsdkEqualizerInterfaces::EqualizerRuntimeSetupInterface { public: /** - * Constructor. - */ - EqualizerRuntimeSetup(); - - /** - * Set equalizer configuration instance. + * Factory method to create an instance of @c EqualizerRuntimeSetupInterface. * - * @param configuration Equalizer configuration instance. + * @param equalizerConfiguration Equalizer configuration instance. + * @param equalizerStorage Equalizer storage instance. + * @param equalizerModeController Equalizer mode controller instance. + * @return An enabled @c EqualizerRuntimeSetup if equalizer is enabled in the configuration instance; otherwise, + * a disabled @c EqualizerRuntimeSetupInterface. */ - void setConfiguration( - std::shared_ptr configuration); + static std::shared_ptr createEqualizerRuntimeSetupInterface( + const std::shared_ptr& equalizerConfiguration, + const std::shared_ptr& equalizerStorage, + const std::shared_ptr& equalizerModeController); /** - * Returns equalizer configuration instance. + * Constructor. * - * @return Equalizer configuration instance. + * @param isEnabled Whether equalizer is enabled; true by default. */ - std::shared_ptr getConfiguration(); + EqualizerRuntimeSetup(bool isEnabled = true); - /** - * Set equalizer state storage instance. - * - * @param storage Equalizer state storage instance. - */ - void setStorage(std::shared_ptr storage); + /// @name EqualizerRuntimeSetupInterface functions + /// @{ + std::shared_ptr getConfiguration() override; - /** - * Returns equalizer state storage instance. - * - * @return Equalizer state storage instance. - */ - std::shared_ptr getStorage(); + std::shared_ptr getStorage() override; - /** - * Set equalizer mode controller instance. - * - * @param modeController Equalizer mode controller instance. - */ - void setModeController( - std::shared_ptr modeController); + std::shared_ptr getModeController() override; - /** - * Returns equalizer mode controller instance. - * - * @return Equalizer mode controller instance. - */ - std::shared_ptr getModeController(); + bool addEqualizer(std::shared_ptr equalizer) override; - /** - * Adds @c EqualizerInterface instance to be used by the SDK. - * - * @param equalizer @c EqualizerInterface instance to be used by the SDK. - */ - void addEqualizer(std::shared_ptr equalizer); + bool addEqualizerControllerListener( + std::shared_ptr listener) override; + + std::list> getAllEqualizers() override; + + std::list> + getAllEqualizerControllerListeners() override; + + bool isEnabled() override; + ///@} /** - * Adds @c EqualizerControllerListenerInterface instance to be used by the SDK. + * Set equalizer configuration instance. * - * @param listener @c EqualizerControllerListenerInterface instance to be used by the SDK. + * @param configuration Equalizer configuration instance. */ - void addEqualizerControllerListener( - std::shared_ptr listener); + void setConfiguration(std::shared_ptr configuration); /** - * Returns a list of all equalizers that are going to be used by the SDK. + * Set equalizer state storage instance. * - * @return List of all equalizers that are going to be used by the SDK. + * @param storage Equalizer state storage instance. */ - std::list> getAllEqualizers(); + void setStorage(std::shared_ptr storage); /** - * Returns a list of all equalizer controller listeners that are going to be used by the SDK. + * Set equalizer mode controller instance. * - * @return List of all equalizer controller listeners that are going to be used by the SDK. + * @param modeController Equalizer mode controller instance. */ - std::list> - getAllEqualizerControllerListeners(); + void setModeController(std::shared_ptr modeController); private: /// Equalizer configuration instance. - std::shared_ptr m_configuration; + std::shared_ptr m_configuration; /// Equalizer mode controller instance. - std::shared_ptr m_modeController; + std::shared_ptr m_modeController; /// Equalizer state storage instance. - std::shared_ptr m_storage; + std::shared_ptr m_storage; /// List of equalizers to be used by the SDK. - std::list> m_equalizers; + std::list> m_equalizers; /// List of listeners to be subscribed to @c EqualizerController. - std::list> + std::list> m_equalizerControllerListeners; + + /// Whether the equalizer is enabled. + bool m_isEnabled; }; } // namespace defaultClient diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/ExternalCapabilitiesBuilderInterface.h b/ApplicationUtilities/DefaultClient/include/DefaultClient/ExternalCapabilitiesBuilderInterface.h index be08bbe9b4..a3a8a9875b 100644 --- a/ApplicationUtilities/DefaultClient/include/DefaultClient/ExternalCapabilitiesBuilderInterface.h +++ b/ApplicationUtilities/DefaultClient/include/DefaultClient/ExternalCapabilitiesBuilderInterface.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -37,9 +38,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -155,15 +156,16 @@ class ExternalCapabilitiesBuilderInterface { * @param sharedDataStream The stream to use which has the audio from microphone. #endif * @param powerResourceManager Object to manage power resource. + * @param softwareComponentReporter Object to report adapters' versions. * @return A list with all capabilities as well as objects that require explicit shutdown. Shutdown will be * performed in the reverse order of occurrence. */ virtual std::pair, std::list>> buildCapabilities( - std::shared_ptr externalMediaPlayer, + std::shared_ptr externalMediaPlayer, std::shared_ptr connectionManager, std::shared_ptr messageSender, - std::shared_ptr exceptionSender, + std::shared_ptr exceptionSender, std::shared_ptr certifiedSender, std::shared_ptr audioFocusManager, std::shared_ptr dataManager, @@ -182,7 +184,8 @@ class ExternalCapabilitiesBuilderInterface { std::shared_ptr commsSpeaker, std::shared_ptr sharedDataStream, #endif - std::shared_ptr powerResourceManager) = 0; + std::shared_ptr powerResourceManager, + std::shared_ptr softwareComponentReporter) = 0; }; } // namespace defaultClient diff --git a/ApplicationUtilities/DefaultClient/include/DefaultClient/StubApplicationAudioPipelineFactory.h b/ApplicationUtilities/DefaultClient/include/DefaultClient/StubApplicationAudioPipelineFactory.h new file mode 100644 index 0000000000..f5e7eabb66 --- /dev/null +++ b/ApplicationUtilities/DefaultClient/include/DefaultClient/StubApplicationAudioPipelineFactory.h @@ -0,0 +1,151 @@ +/* + * 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_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_STUBAPPLICATIONAUDIOPIPELINEFACTORY_H_ +#define ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_STUBAPPLICATIONAUDIOPIPELINEFACTORY_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace alexaClientSDK { +namespace defaultClient { + +/** + * This is a factory class that can be used during the transition to using the manufactory for creating + * media players and related interfaces. Pre-built media players and speakers can be added to this factory, and the + * factory will return the pre-built @c ApplicationMediaInterfaces with the specified name when + * createApplicationMediaInterfaces(...) is called. + * + * Other factories such as acsdkGstreamerAudioPipelineFactory should be preferred. + * + * This factory will register the pre-built speakers with SpeakerManager, but unlike the real factory implementations, + * it will not register equalizers with EqualizerRuntimeSetup nor media players with the CaptionManager. Applications + * that use this stub factory are responsible for doing so outside of this factory. + * + */ +class StubApplicationAudioPipelineFactory + : public acsdkApplicationAudioPipelineFactoryInterfaces::ApplicationAudioPipelineFactoryInterface { +public: + /** + * Creates a new @c StubApplicationAudioPipelineFactory. + * + * @param channelVolumeFactory The @c ChannelVolumeFactoryInterface to use for creating channel volume interfaces. + * @return A new @c StubApplicationAudioPipelineFactory. + */ + static std::shared_ptr create( + const std::shared_ptr& channelVolumeFactory); + + /** + * Adds the @c SpeakerManagerInterface for registering speakers. + * + * @param speakerManager The @c SpeakerManagerInterface with which to register speakers. + */ + void addSpeakerManager(std::shared_ptr& speakerManager); + + /** + * Adds the @c CaptionManagerInterface for adding captionable media players. + * + * @param captionManager The @c CaptionManagerInterface with which to register speakers. + */ + void addCaptionManager(std::shared_ptr& captionManager); + + /** + * Adds application media interfaces to the factory for later retrieval when createApplicationMediaInterfaces + * is called. + * + * @param name The name of the media player. This name is used as the key to retrieve the correct application media + * interfaces when createApplicationMediaInterfaces is called. + * @param mediaPlayer The @c MediaPlayerInterface to add. + * @param speaker The @c SpeakerInterface to add. + */ + bool addApplicationMediaInterfaces( + const std::string& name, + const std::shared_ptr& mediaPlayer, + const std::shared_ptr& speaker); + + /** + * Adds a vector of application media interfaces to the factory for later retrieval when + * createApplicationMediaInterfaces is called. + * + * @param name The name of the media players. This name is used as the key to retrieve the correct application media + * interfaces when createApplicationMediaInterfaces is called. + * @param mediaInterfaces A vector of media player/speaker pairs to add. + */ + bool addApplicationMediaInterfaces( + const std::string& name, + std::vector, + std::shared_ptr>> mediaInterfaces); + + /// @name ApplicationAudioPipelineFactoryInterface + /// @{ + std::shared_ptr createApplicationMediaInterfaces( + const std::string& name, + bool equalizerAvailable, + bool enableLiveMode, + bool isCaptionable, + avsCommon::sdkInterfaces::ChannelVolumeInterface::Type channelVolumeType, + std::function volumeCurve) override; + std::shared_ptr + createPooledApplicationMediaInterfaces( + const std::string& name, + int numMediaPlayers, + bool equalizerAvailable, + bool enableLiveMode, + bool isCaptionable, + avsCommon::sdkInterfaces::ChannelVolumeInterface::Type channelVolumeType, + std::function volumeCurve) override; + /// @} + +private: + /** + * Constructor. + */ + StubApplicationAudioPipelineFactory( + const std::shared_ptr& channelVolumeFactory); + + /// Mutex to synchronize access to m_applicationMediaInterfacesMap. + std::mutex m_applicationMediaInterfacesMapMutex; + + /** + * Map of name to vector of @c ApplicationMediaInterfaces. The factory will pop an @c ApplicationMediaInterfaces + * off the corresponding queue when createApplicationMediaInterfaces is called with the name. + */ + std::unordered_map>> + m_applicationMediaInterfacesMap; + + /// The @c SpeakerManagerInterface with which to register speakers. + std::shared_ptr m_speakerManager; + + /// The @c CaptionManagerInterface with which to register captionable media players. + std::shared_ptr m_captionManager; + + /// The @c ChannelVolumeFactoryInterface with which to create channel volume interfaces. + std::shared_ptr m_channelVolumeFactory; +}; + +} // namespace defaultClient +} // namespace alexaClientSDK + +#endif // ALEXA_CLIENT_SDK_APPLICATIONUTILITIES_DEFAULTCLIENT_INCLUDE_DEFAULTCLIENT_STUBAPPLICATIONAUDIOPIPELINEFACTORY_H_ diff --git a/ApplicationUtilities/DefaultClient/src/CMakeLists.txt b/ApplicationUtilities/DefaultClient/src/CMakeLists.txt index 04cfe499b0..594854f3b0 100644 --- a/ApplicationUtilities/DefaultClient/src/CMakeLists.txt +++ b/ApplicationUtilities/DefaultClient/src/CMakeLists.txt @@ -4,8 +4,10 @@ add_definitions("-DACSDK_LOG_MODULE=defaultClient") add_library(DefaultClient SHARED ConnectionRetryTrigger.cpp DefaultClient.cpp + DefaultClientComponent.cpp DeviceSettingsManagerBuilder.cpp EqualizerRuntimeSetup.cpp + StubApplicationAudioPipelineFactory.cpp ) target_include_directories(DefaultClient PUBLIC "${DefaultClient_SOURCE_DIR}/include" @@ -19,6 +21,10 @@ endif() target_link_libraries(DefaultClient ACL + acsdkApplicationAudioPipelineFactoryInterfaces + acsdkCore + acsdkManufactory + acsdkShared ADSL AFML AIP @@ -28,12 +34,11 @@ target_link_libraries(DefaultClient AVSCommon AVSSystem Captions + CaptionsComponent ContextManager DeviceSettings acsdkDoNotDisturb Endpoints - Equalizer - ExternalMediaPlayer InteractionModel InterruptModel acsdkNotifications @@ -48,6 +53,9 @@ target_link_libraries(DefaultClient acsdkAlerts acsdkAudioPlayer acsdkBluetooth + acsdkEqualizer + acsdkExternalMediaPlayer + acsdkStartupManagerInterfaces ) if (CAPTIONS) diff --git a/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp b/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp index a5bef1f878..c8b3fd1030 100644 --- a/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp +++ b/ApplicationUtilities/DefaultClient/src/DefaultClient.cpp @@ -13,23 +13,27 @@ * permissions and limitations under the License. */ +#include #include #include #include +#include #include #include #include +#include #include #include #include