avs-device-sdk/ContextManager/include/ContextManager/ContextManager.h

355 lines
15 KiB
C++

/*
* 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_CONTEXTMANAGER_INCLUDE_CONTEXTMANAGER_CONTEXTMANAGER_H_
#define ALEXA_CLIENT_SDK_CONTEXTMANAGER_INCLUDE_CONTEXTMANAGER_CONTEXTMANAGER_H_
#include <atomic>
#include <list>
#include <memory>
#include <utility>
#include <mutex>
#include <unordered_map>
#include <unordered_set>
#include <AVSCommon/AVS/CapabilityTag.h>
#include <AVSCommon/AVS/StateRefreshPolicy.h>
#include <AVSCommon/SDKInterfaces/ContextManagerInterface.h>
#include <AVSCommon/SDKInterfaces/ContextRequesterInterface.h>
#include <AVSCommon/SDKInterfaces/Endpoints/EndpointIdentifier.h>
#include <AVSCommon/SDKInterfaces/StateProviderInterface.h>
#include <AVSCommon/Utils/DeviceInfo.h>
#include <AVSCommon/Utils/Metrics/MetricRecorderInterface.h>
#include <AVSCommon/Utils/Optional.h>
#include <AVSCommon/Utils/Threading/Executor.h>
#include <AVSCommon/Utils/Timing/MultiTimer.h>
namespace alexaClientSDK {
namespace contextManager {
/**
* Class manages the requests for getting context from @c ContextRequesters and updating the state from
* @c StateProviders.
*/
class ContextManager : public avsCommon::sdkInterfaces::ContextManagerInterface {
public:
/**
* Create a new @c ContextManager instance.
*
* @param deviceInfo Structure used to retrieve the default endpoint id.
* @param multiTimer Object used to schedule request timeout.
* @param metricRecorder The metric recorder.
* @return Returns a new @c ContextManager.
*/
static std::shared_ptr<ContextManagerInterface> createContextManagerInterface(
const std::shared_ptr<avsCommon::utils::DeviceInfo>& deviceInfo,
const std::shared_ptr<avsCommon::utils::timing::MultiTimer>& multiTimer =
std::make_shared<avsCommon::utils::timing::MultiTimer>(),
const std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface>& metricRecorder = nullptr);
/**
* Create a new @c ContextManager instance.
*
* @deprecated
* @param deviceInfo Structure used to retrieve the default endpoint id.
* @param multiTimer Object used to schedule request timeout.
* @param metricRecorder The metric recorder.
* @return Returns a new @c ContextManager.
*/
static std::shared_ptr<ContextManager> create(
const avsCommon::utils::DeviceInfo& deviceInfo,
std::shared_ptr<avsCommon::utils::timing::MultiTimer> multiTimer =
std::make_shared<avsCommon::utils::timing::MultiTimer>(),
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> metricRecorder = nullptr);
/// Destructor.
~ContextManager() override;
/// @name ContextManagerInterface methods.
/// @{
void setStateProvider(
const avsCommon::avs::CapabilityTag& stateProviderName,
std::shared_ptr<avsCommon::sdkInterfaces::StateProviderInterface> stateProvider) override;
void addStateProvider(
const avsCommon::avs::CapabilityTag& capabilityIdentifier,
std::shared_ptr<avsCommon::sdkInterfaces::StateProviderInterface> stateProvider) override;
void removeStateProvider(const avsCommon::avs::CapabilityTag& capabilityIdentifier) override;
avsCommon::sdkInterfaces::SetStateResult setState(
const avsCommon::avs::CapabilityTag& stateProviderName,
const std::string& jsonState,
const avsCommon::avs::StateRefreshPolicy& refreshPolicy,
const avsCommon::sdkInterfaces::ContextRequestToken stateRequestToken = 0) override;
avsCommon::sdkInterfaces::ContextRequestToken getContext(
std::shared_ptr<avsCommon::sdkInterfaces::ContextRequesterInterface> contextRequester,
const std::string& endpointId,
const std::chrono::milliseconds& timeout) override;
avsCommon::sdkInterfaces::ContextRequestToken getContextWithoutReportableStateProperties(
std::shared_ptr<avsCommon::sdkInterfaces::ContextRequesterInterface> contextRequester,
const std::string& endpointId,
const std::chrono::milliseconds& timeout) override;
void reportStateChange(
const avsCommon::avs::CapabilityTag& capabilityIdentifier,
const avsCommon::avs::CapabilityState& capabilityState,
avsCommon::sdkInterfaces::AlexaStateChangeCauseType cause) override;
void provideStateResponse(
const avsCommon::avs::CapabilityTag& capabilityIdentifier,
const avsCommon::avs::CapabilityState& capabilityState,
avsCommon::sdkInterfaces::ContextRequestToken stateRequestToken) override;
void provideStateUnavailableResponse(
const avsCommon::avs::CapabilityTag& capabilityIdentifier,
avsCommon::sdkInterfaces::ContextRequestToken stateRequestToken,
bool isEndpointUnreachable) override;
void addContextManagerObserver(
std::shared_ptr<avsCommon::sdkInterfaces::ContextManagerObserverInterface> observer) override;
void removeContextManagerObserver(
const std::shared_ptr<avsCommon::sdkInterfaces::ContextManagerObserverInterface>& observer) override;
/// @}
private: // Private type declarations.
/**
* This class has all the information about a @c StateProviderInterface needed by the contextManager.
*/
struct StateInfo {
/// Pointer to the StateProvider.
std::shared_ptr<avsCommon::sdkInterfaces::StateProviderInterface> stateProvider;
/// The state of the capability.
avsCommon::utils::Optional<avsCommon::avs::CapabilityState> capabilityState;
/// Whether this capability state should always be reported independently of isRetrievable.
/// @note This is used for capabilities that use the old interface with refresh token.
bool legacyCapability;
/// The refresh policy which is only used for legacy capabilities.
avsCommon::avs::StateRefreshPolicy refreshPolicy;
/**
* Constructor.
*
* @param initStateProvider The @c StateProviderInterface.
* @param initJsonState The state of the @c StateProviderInterface.
* @param refreshPolicy The refresh policy of the state of the @c StateProviderInterface.
*
* @deprecated @c StateRefreshPolicy has been deprecated.
*/
StateInfo(
std::shared_ptr<avsCommon::sdkInterfaces::StateProviderInterface> initStateProvider = nullptr,
const std::string& initJsonState = "",
avsCommon::avs::StateRefreshPolicy initRefreshPolicy = avsCommon::avs::StateRefreshPolicy::ALWAYS);
/**
* Constructor.
*
* @param initStateProvider The @c StateProviderInterface.
* @param initCapabilityState The initial capability state.
*/
StateInfo(
std::shared_ptr<avsCommon::sdkInterfaces::StateProviderInterface> initStateProvider,
const avsCommon::utils::Optional<avsCommon::avs::CapabilityState>& initCapabilityState);
/**
* Copy constructor.
*/
StateInfo(const StateInfo&) = default;
};
/// Map of capabilities and their last known state.
using CapabilitiesState = std::unordered_map<avsCommon::avs::CapabilityTag, StateInfo>;
/// Alias for endpoint id.
using EndpointIdentifier = avsCommon::sdkInterfaces::endpoints::EndpointIdentifier;
/**
* Structure used to save information about a request.
*/
struct RequestTracker {
/// The token returned by the @c MultiTimer.
avsCommon::utils::timing::MultiTimer::Token timerToken;
/// The context requester.
std::shared_ptr<avsCommon::sdkInterfaces::ContextRequesterInterface> contextRequester;
/// If Reportable Properties should be skipped for this request.
bool skipReportableStateProperties;
/**
* Default Constructor.
*/
RequestTracker();
/**
* Constructor.
* @param timerToken The token returned by the @c MultiTimer.
* @param contextRequester The @c ContextRequesterInterface pointer.
* @param skipReportableProperties The boolean indicating if the reportable properties should be skipped for
* this request.
*/
RequestTracker(
avsCommon::utils::timing::MultiTimer::Token timerToken,
std::shared_ptr<avsCommon::sdkInterfaces::ContextRequesterInterface> contextRequester,
bool skipReportableProperties);
};
private: // Private method declarations.
/**
* Constructor.
*
* @param defaultEndpointId The default endpoint used for legacy methods where no endpoint id is provided.
* @param multiTimer Object used to schedule request timeout.
* @param metricRecorder The metric recorder.
*/
ContextManager(
const std::string& defaultEndpointId,
std::shared_ptr<avsCommon::utils::timing::MultiTimer> multiTimer,
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> metricRecorder);
///
/**
* Updates the state of a capability.
*
* @param capabilityIdentifier The capability identifier.
* @param jsonState The state of the @c StateProviderInterface.
* @param refreshPolicy The refresh policy for the state.
* @deprecated @c StateRefreshPolicy has been deprecated.
*/
void updateCapabilityState(
const avsCommon::avs::CapabilityTag& capabilityIdentifier,
const std::string& jsonState,
const avsCommon::avs::StateRefreshPolicy& refreshPolicy);
/**
* Updates the state of a capability.
*
* @param capabilityIdentifier The capability identifier.
* @param capabilityState The capability state.
* @deprecated @c StateRefreshPolicy has been deprecated.
*/
void updateCapabilityState(
const avsCommon::avs::CapabilityTag& capabilityIdentifier,
const avsCommon::avs::CapabilityState& capabilityState);
/**
* Request the @c ContextManager for context.
*
* @param contextRequester The context requester asking for context.
* @param endpointId The @c endpointId used to select which context is being requested.
* @param timeout The maximum time this request should take. After the timeout, the context manager will abort the
* request.
* @param skipReportableStateProperties The flag which indicates if ReportableStateProperties can be ignored from
* the context response.
* @return A token that can be used to correlate this request with the context response.
*/
avsCommon::sdkInterfaces::ContextRequestToken getContextInternal(
std::shared_ptr<avsCommon::sdkInterfaces::ContextRequesterInterface> contextRequester,
const std::string& endpointId,
const std::chrono::milliseconds& timeout,
bool skipReportableStateProperties);
/**
* This method returns a callback which should be invoked once the context is ready.
* If the context is not ready, this method will return a no-op function.
*
* @note The callback method that is returned should only be called outside of a lock to prevent deadlock scenarios.
*
* @note If the context is ready, the method also removes the request from the pending requests map.
*
* @param requestToken The request token associated with the context request.
* @param endpointId The endpointId associated with the context request.
* @return A callback method to notify the context requester when the context is ready. An empty no-op function
* if the context is not ready.
*/
std::function<void()> getContextAvailableCallbackIfReadyLocked(
avsCommon::sdkInterfaces::ContextRequestToken requestToken,
const avsCommon::sdkInterfaces::endpoints::EndpointIdentifier& endpointId);
/**
* This method returns a callback which should be invoked once there is a context failure.
* If the contextRequester is invalid, this method returns a no-op function.
*
* @note The callback method that is returned should only be called outside of a lock to prevent deadlock scenarios.
*
* @note The method also cleans up the request from the pending requests map.
*
* @param requestToken The request token associated with the context request.
* @param error The @c ContextRequestError to be notified to the requester.
* @return A callback method to notify the context requester of a context fetch failure. An empty no-op function if
* the context requester is invalid.
*/
std::function<void()> getContextFailureCallbackLocked(
avsCommon::sdkInterfaces::ContextRequestToken requestToken,
avsCommon::sdkInterfaces::ContextRequestError error);
/**
* Generate a request token.
*
* @return A new unique request token.
*/
inline avsCommon::sdkInterfaces::ContextRequestToken generateToken();
private: // Member variable declarations
/// Mutex used to guard m_endpointsState access.
std::mutex m_endpointsStateMutex;
/// Map of state provider namespace and name to the state information. @c m_stateProviderMutex must be acquired
/// before accessing the map.
std::unordered_map<EndpointIdentifier, CapabilitiesState> m_endpointsState;
/// Mutex used to guard the pending state requests. This is only needed because of @c setState.
std::mutex m_requestsMutex;
/// The metric recorder.
std::shared_ptr<avsCommon::utils::metrics::MetricRecorderInterface> m_metricRecorder;
/// The request token counter.
std::atomic<unsigned int> m_requestCounter;
/// Map of pending states per ongoing request.
std::unordered_map<unsigned int, std::unordered_set<avsCommon::avs::CapabilityTag>> m_pendingStateRequest;
/// Map of requester per ongoing request and their respective tokens.
std::unordered_map<avsCommon::sdkInterfaces::ContextRequestToken, RequestTracker> m_pendingRequests;
/// Mutex used to guard the observers.
std::mutex m_observerMutex;
/// List of observers.
std::list<std::shared_ptr<avsCommon::sdkInterfaces::ContextManagerObserverInterface>> m_observers;
/// Whether the contextManager is shutting down.
std::atomic_bool m_shutdown;
/// Endpoint identifier used to keep backward compatibility with capabilities without endpoint information.
const std::string m_defaultEndpointId;
/// Timer to handler timeouts.
std::shared_ptr<avsCommon::utils::timing::MultiTimer> m_multiTimer;
/// Executor used to handle the context requests.
avsCommon::utils::threading::Executor m_executor;
};
} // namespace contextManager
} // namespace alexaClientSDK
#endif // ALEXA_CLIENT_SDK_CONTEXTMANAGER_INCLUDE_CONTEXTMANAGER_CONTEXTMANAGER_H_