avs-device-sdk/AFML/src/Channel.cpp

346 lines
12 KiB
C++

/*
* Copyright 2017-2020 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 "AFML/Channel.h"
#include <AVSCommon/Utils/Logger/Logger.h>
namespace alexaClientSDK {
namespace afml {
using namespace avsCommon::avs;
using namespace avsCommon::sdkInterfaces;
/// String to identify log entries originating from this file.
static const std::string TAG("Channel");
/**
* 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)
Channel::State::State(const std::string& name) :
name{name},
focusState{FocusState::NONE},
timeAtIdle{std::chrono::steady_clock::now()} {
}
Channel::State::State() : focusState{FocusState::NONE}, timeAtIdle{std::chrono::steady_clock::now()} {
}
Channel::Channel(const std::string& name, const unsigned int priority, bool isVirtual) :
m_priority{priority},
m_isVirtual{isVirtual},
m_state{name} {
}
const std::string& Channel::getName() const {
return m_state.name;
}
unsigned int Channel::getPriority() const {
return m_priority;
}
bool Channel::setFocus(FocusState focus, MixingBehavior behavior, bool forceUpdate) {
std::unique_lock<std::mutex> lock(m_mutex);
bool focusChanged = (m_state.focusState != focus);
auto primaryActivity = getPrimaryActivityLocked();
bool mixingBehaviorChanged = primaryActivity && (primaryActivity->getMixingBehavior() != behavior);
if (!forceUpdate && !focusChanged && !mixingBehaviorChanged) {
return false;
}
ACSDK_DEBUG5(LX(__func__)
.d("name", m_state.name)
.d("newfocusState", focus)
.d("prevfocusState", m_state.focusState)
.d("newMixingBehavior", behavior)
.d("forceUpdate", forceUpdate));
m_state.focusState = focus;
if (m_state.focusState == FocusState::NONE) {
m_state.timeAtIdle = std::chrono::steady_clock::now();
}
// Update Channel State Updates
addToChannelUpdatesLocked(m_state.interfaceName, m_state.focusState);
lock.unlock();
// Notify all activities of the new focus state for this channel
notifyActivities(behavior, focus);
return true;
}
void Channel::setPrimaryActivity(std::shared_ptr<FocusManagerInterface::Activity> activity) {
std::unique_lock<std::mutex> lock(m_mutex);
if (!activity) {
ACSDK_ERROR(LX("setPrimaryActivityFailed").m("Null Activity"));
return;
}
ACSDK_DEBUG5(LX(__func__).d("Interface", activity->getInterface()));
if (!m_activities.empty()) {
processPolicyLocked(activity, m_activities.front());
}
// Establish the activity.
m_activities.push_front(activity);
updateChannelInterfaceLocked();
}
bool Channel::releaseActivity(std::shared_ptr<ChannelObserverInterface> observer) {
if (observer == nullptr) {
ACSDK_ERROR(LX("releaseActivityFailed").d("reason", "observer is null."));
return false;
}
std::unique_lock<std::mutex> lock(m_mutex);
for (auto it = m_activities.begin(); it != m_activities.end(); ++it) {
if ((*it)->getChannelObserver() == observer) {
bool success = removeActivityHelperLocked(it);
if (success) {
// No change in observer or activity if remove fails.
addToChannelUpdatesLocked(m_state.interfaceName, m_state.focusState);
}
return success;
}
}
ACSDK_DEBUG0(LX("releaseActivityFailed").m("Observer not found"));
return false;
}
bool Channel::releaseActivity(const std::string& interfaceName) {
std::unique_lock<std::mutex> lock(m_mutex);
for (auto it = m_activities.begin(); it != m_activities.end(); ++it) {
if ((*it)->getInterface() == interfaceName) {
bool success = removeActivityHelperLocked(it);
if (success) {
// Update Channel State Updates.
addToChannelUpdatesLocked(m_state.interfaceName, m_state.focusState);
}
return success;
}
}
return false;
}
bool Channel::isActive() {
std::unique_lock<std::mutex> lock(m_mutex);
return !m_activities.empty();
}
bool Channel::operator>(const Channel& rhs) const {
return m_priority < rhs.getPriority();
}
std::string Channel::getInterface() const {
std::lock_guard<std::mutex> lock(m_mutex);
return m_state.interfaceName;
}
Channel::State Channel::getState() const {
std::lock_guard<std::mutex> lock(m_mutex);
return m_state;
}
std::vector<Channel::State> Channel::getActivityUpdates() {
std::unique_lock<std::mutex> lock(m_mutex);
auto activityUpdatesRet = m_activityUpdates;
m_activityUpdates.clear();
return activityUpdatesRet;
}
std::shared_ptr<FocusManagerInterface::Activity> Channel::getPrimaryActivity() {
std::unique_lock<std::mutex> lock(m_mutex);
return getPrimaryActivityLocked();
}
std::shared_ptr<FocusManagerInterface::Activity> Channel::getPrimaryActivityLocked() const {
if (m_activities.empty()) {
return nullptr;
}
return *(m_activities.begin());
}
std::shared_ptr<FocusManagerInterface::Activity> Channel::getActivity(const std::string& interfaceName) {
std::unique_lock<std::mutex> lock(m_mutex);
for (const auto& it : m_activities) {
if (it->getInterface() == interfaceName) return it;
}
return nullptr;
}
std::vector<std::string> Channel::getInterfaceList() const {
std::vector<std::string> listOfInterface = {};
for (const auto& activity : m_activities) {
listOfInterface.push_back(activity->getInterface());
}
return listOfInterface;
}
void Channel::notifyActivities(MixingBehavior behavior, FocusState focusState) {
std::unique_lock<std::mutex> lock(m_mutex);
if (m_activities.empty()) {
ACSDK_WARN(LX("notifyActivitiesFailed").m("No Associated Activities Found"));
return;
}
auto activitiesCopy = m_activities;
lock.unlock();
auto activityIt = activitiesCopy.begin();
// inform the primary activity with the MixingBehavior
(*activityIt)->notifyObserver(focusState, behavior);
activityIt++;
// all other secondary activities must be PAUSED + BACKGROUND
for (; activityIt != activitiesCopy.end(); activityIt++) {
(*activityIt)->notifyObserver(FocusState::BACKGROUND, MixingBehavior::MUST_PAUSE);
}
}
bool Channel::releaseActivityLocked(
std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> activityToRelease) {
if (!activityToRelease) {
ACSDK_ERROR(LX("releaseActivityLockedFailed").m("Null activityToRelease"));
return false;
}
auto priorActivityIt = std::find(m_activities.begin(), m_activities.end(), activityToRelease);
if (priorActivityIt != m_activities.end()) {
return removeActivityHelperLocked(priorActivityIt);
}
return false;
}
bool Channel::removeActivityHelperLocked(
std::list<std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity>>::iterator
activityToRemoveIt) {
ACSDK_DEBUG5(LX(__func__).d("interface", (*activityToRemoveIt)->getInterface()));
auto isRemovingPatienceReceiver = false;
if (m_patienceReceiver == *activityToRemoveIt) {
isRemovingPatienceReceiver = true;
}
// Timer is active AND the job being removed is one of the patience Activities.
if (m_patienceTimer.isActive() &&
(m_patienceInitiator == *activityToRemoveIt || m_patienceReceiver == *activityToRemoveIt)) {
m_patienceTimer.stop();
ACSDK_DEBUG9(LX(__func__).d("status", "Patience Timer Stopped"));
m_patienceInitiator = nullptr;
m_patienceReceiver = nullptr;
}
// If the activity to remove is the last activity, then update the time at idle for this channel
if (m_activities.size() == 1) {
m_state.timeAtIdle = std::chrono::steady_clock::now();
}
// No need to update Channel updates for removing patience receiver.
if (!isRemovingPatienceReceiver) {
// Add the current State and override the FocusState with NONE.
addToChannelUpdatesLocked((*activityToRemoveIt)->getInterface(), FocusState::NONE);
}
// Report NONE only to the single Activity that is removed
(*activityToRemoveIt)->notifyObserver(FocusState::NONE, MixingBehavior::MUST_STOP);
m_activities.erase(activityToRemoveIt);
updateChannelInterfaceLocked();
return true;
}
void Channel::addToChannelUpdatesLocked(const std::string& interfaceName, avsCommon::avs::FocusState focusState) {
if (m_state.interfaceName.empty()) {
return;
}
// Only add to Channel updates if channel is not virtual.
if (!m_isVirtual) {
auto state = m_state;
state.focusState = focusState;
state.interfaceName = interfaceName;
m_activityUpdates.push_back(state);
ACSDK_DEBUG0(LX(__func__).d("interface", state.interfaceName).d("focusState", state.focusState));
}
}
void Channel::processPolicyLocked(
std::shared_ptr<FocusManagerInterface::Activity> incomingActivity,
std::shared_ptr<FocusManagerInterface::Activity> currentActivity) {
if (!incomingActivity || !currentActivity) {
ACSDK_ERROR(LX("processPolicyLockedFailed").m("Null Activities"));
return;
}
if (incomingActivity->getInterface() == currentActivity->getInterface()) {
// Both incoming and current activity has identical interface. Remove the current activity regardless of policy.
releaseActivityLocked(currentActivity);
return;
}
if (m_patienceTimer.isActive()) {
// A new Activity is incoming. If there is an ongoing patience release, remove the receiver immediately.
// Any persistent initiator or receiver will not be removed.
m_patienceTimer.stop();
ACSDK_DEBUG9(LX(__func__).d("status", "Patience Release Timer Stopped"));
releaseActivityLocked(m_patienceReceiver);
m_patienceReceiver = nullptr;
}
if (incomingActivity->getPatienceDuration().count() > 0) {
// Incoming valid patience Activity
ACSDK_DEBUG9(LX(__func__).d("status", "Patience Timer Started"));
addToChannelUpdatesLocked(currentActivity->getInterface(), FocusState::NONE);
auto patienceDuration = incomingActivity->getPatienceDuration();
m_patienceTimer.start(patienceDuration, std::bind(&Channel::patienceTimerCallback, this, currentActivity));
m_patienceInitiator = incomingActivity;
m_patienceReceiver = currentActivity;
} else {
if (m_patienceInitiator != nullptr) {
// No valid patience duration, release the initiator.
releaseActivityLocked(m_patienceInitiator);
m_patienceInitiator = nullptr;
}
releaseActivityLocked(currentActivity);
}
}
void Channel::patienceTimerCallback(
std::shared_ptr<avsCommon::sdkInterfaces::FocusManagerInterface::Activity> activity) {
std::unique_lock<std::mutex> lock(m_mutex);
ACSDK_DEBUG9(LX(__func__).d("status", "Patience Release Timer Triggered"));
releaseActivityLocked(std::move(activity));
// No need to modify channel updates since it was Already reported
// when patienceInitiator came into focus.
}
void Channel::updateChannelInterfaceLocked() {
if (!m_activities.empty()) {
m_state.interfaceName = m_activities.front()->getInterface();
} else {
m_state.interfaceName = "";
}
}
} // namespace afml
} // namespace alexaClientSDK