blob: 0990294e571f3b37779a486016b5f64cbf92b553 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "osp/impl/service_listener_impl.h"
#include <algorithm>
#include "platform/base/error.h"
#include "util/osp_logging.h"
namespace openscreen {
namespace osp {
namespace {
bool IsTransitionValid(ServiceListener::State from, ServiceListener::State to) {
switch (from) {
case ServiceListener::State::kStopped:
return to == ServiceListener::State::kStarting ||
to == ServiceListener::State::kStopping;
case ServiceListener::State::kStarting:
return to == ServiceListener::State::kRunning ||
to == ServiceListener::State::kStopping ||
to == ServiceListener::State::kSuspended;
case ServiceListener::State::kRunning:
return to == ServiceListener::State::kSuspended ||
to == ServiceListener::State::kSearching ||
to == ServiceListener::State::kStopping;
case ServiceListener::State::kStopping:
return to == ServiceListener::State::kStopped;
case ServiceListener::State::kSearching:
return to == ServiceListener::State::kRunning ||
to == ServiceListener::State::kSuspended ||
to == ServiceListener::State::kStopping;
case ServiceListener::State::kSuspended:
return to == ServiceListener::State::kRunning ||
to == ServiceListener::State::kSearching ||
to == ServiceListener::State::kStopping;
default:
OSP_DCHECK(false) << "unknown ServiceListener::State value: "
<< static_cast<int>(from);
break;
}
return false;
}
} // namespace
ServiceListenerImpl::Delegate::Delegate() = default;
ServiceListenerImpl::Delegate::~Delegate() = default;
void ServiceListenerImpl::Delegate::SetListenerImpl(
ServiceListenerImpl* listener) {
OSP_DCHECK(!listener_);
listener_ = listener;
}
ServiceListenerImpl::ServiceListenerImpl(Delegate* delegate)
: delegate_(delegate) {
delegate_->SetListenerImpl(this);
}
ServiceListenerImpl::~ServiceListenerImpl() = default;
void ServiceListenerImpl::OnReceiverAdded(const ServiceInfo& info) {
receiver_list_.OnReceiverAdded(info);
for (auto* observer : observers_) {
observer->OnReceiverAdded(info);
}
}
void ServiceListenerImpl::OnReceiverChanged(const ServiceInfo& info) {
const Error changed_error = receiver_list_.OnReceiverChanged(info);
if (changed_error.ok()) {
for (auto* observer : observers_) {
observer->OnReceiverChanged(info);
}
}
}
void ServiceListenerImpl::OnReceiverRemoved(const ServiceInfo& info) {
const Error removed_error = receiver_list_.OnReceiverRemoved(info);
if (removed_error.ok()) {
for (auto* observer : observers_) {
observer->OnReceiverRemoved(info);
}
}
}
void ServiceListenerImpl::OnAllReceiversRemoved() {
const Error removed_all_error = receiver_list_.OnAllReceiversRemoved();
if (removed_all_error.ok()) {
for (auto* observer : observers_) {
observer->OnAllReceiversRemoved();
}
}
}
void ServiceListenerImpl::OnError(ServiceListenerError error) {
last_error_ = error;
for (auto* observer : observers_) {
observer->OnError(error);
}
}
bool ServiceListenerImpl::Start() {
if (state_ != State::kStopped)
return false;
state_ = State::kStarting;
delegate_->StartListener();
return true;
}
bool ServiceListenerImpl::StartAndSuspend() {
if (state_ != State::kStopped)
return false;
state_ = State::kStarting;
delegate_->StartAndSuspendListener();
return true;
}
bool ServiceListenerImpl::Stop() {
if (state_ == State::kStopped || state_ == State::kStopping)
return false;
state_ = State::kStopping;
delegate_->StopListener();
return true;
}
bool ServiceListenerImpl::Suspend() {
if (state_ != State::kRunning && state_ != State::kSearching &&
state_ != State::kStarting) {
return false;
}
delegate_->SuspendListener();
return true;
}
bool ServiceListenerImpl::Resume() {
if (state_ != State::kSuspended && state_ != State::kSearching)
return false;
delegate_->ResumeListener();
return true;
}
bool ServiceListenerImpl::SearchNow() {
if (state_ != State::kRunning && state_ != State::kSuspended)
return false;
delegate_->SearchNow(state_);
return true;
}
void ServiceListenerImpl::AddObserver(Observer* observer) {
OSP_DCHECK(observer);
observers_.push_back(observer);
}
void ServiceListenerImpl::RemoveObserver(Observer* observer) {
// TODO(btolsch): Consider writing an ObserverList in base/ for things like
// CHECK()ing that the list is empty on destruction.
observers_.erase(std::remove(observers_.begin(), observers_.end(), observer),
observers_.end());
}
const std::vector<ServiceInfo>& ServiceListenerImpl::GetReceivers() const {
return receiver_list_.receivers();
}
void ServiceListenerImpl::SetState(State state) {
OSP_DCHECK(IsTransitionValid(state_, state));
state_ = state;
if (!observers_.empty()) {
MaybeNotifyObservers();
}
}
void ServiceListenerImpl::MaybeNotifyObservers() {
OSP_DCHECK(!observers_.empty());
switch (state_) {
case State::kRunning:
for (auto* observer : observers_) {
observer->OnStarted();
}
break;
case State::kStopped:
for (auto* observer : observers_) {
observer->OnStopped();
}
break;
case State::kSuspended:
for (auto* observer : observers_) {
observer->OnSuspended();
}
break;
case State::kSearching:
for (auto* observer : observers_) {
observer->OnSearching();
}
break;
default:
break;
}
}
} // namespace osp
} // namespace openscreen