blob: e00e54053887ded30f02b89496bae3fde75fac64 [file] [log] [blame]
// Copyright 2022 The Fuchsia 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 "src/media/audio/services/device_registry/observer_server.h"
#include <fidl/fuchsia.audio.device/cpp/fidl.h>
#include <fidl/fuchsia.hardware.audio/cpp/fidl.h>
#include <lib/fit/internal/result.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/errors.h>
#include "src/media/audio/services/device_registry/audio_device_registry.h"
#include "src/media/audio/services/device_registry/device.h"
#include "src/media/audio/services/device_registry/logging.h"
namespace media_audio {
// static
std::shared_ptr<ObserverServer> ObserverServer::Create(
std::shared_ptr<const FidlThread> thread,
fidl::ServerEnd<fuchsia_audio_device::Observer> server_end,
std::shared_ptr<const Device> device) {
ADR_LOG_STATIC(kLogObserverServerMethods);
return BaseFidlServer::Create(std::move(thread), std::move(server_end), std::move(device));
}
ObserverServer::ObserverServer(std::shared_ptr<const Device> device) : device_(std::move(device)) {
ADR_LOG_METHOD(kLogObjectLifetimes);
// TODO(https://fxbug.dev/42068381): Consider Health-check if this can change post-initialization.
++count_;
LogObjectCounts();
}
ObserverServer::~ObserverServer() {
ADR_LOG_METHOD(kLogObjectLifetimes);
--count_;
LogObjectCounts();
}
void ObserverServer::DeviceHasError() {
ADR_LOG_METHOD(kLogObserverServerMethods || kLogNotifyMethods);
device_has_error_ = true;
DeviceIsRemoved();
}
// Called when the Device shuts down first.
void ObserverServer::DeviceIsRemoved() {
ADR_LOG_METHOD(kLogObserverServerMethods || kLogNotifyMethods);
Shutdown(ZX_ERR_PEER_CLOSED);
// We don't explicitly clear our shared_ptr<Device> reference, to ensure we destruct first.
}
void ObserverServer::WatchGainState(WatchGainStateCompleter::Sync& completer) {
ADR_LOG_METHOD(kLogObserverServerMethods);
if (device_has_error_) {
ADR_WARN_METHOD() << "Device encountered an error and will be removed";
completer.Reply(fit::error<fuchsia_audio_device::ObserverWatchGainStateError>(
fuchsia_audio_device::ObserverWatchGainStateError::kDeviceError));
return;
}
FX_CHECK(device_);
if (!device_->is_stream_config()) {
ADR_WARN_METHOD() << "This method is not supported for this device type";
completer.Reply(fit::error<fuchsia_audio_device::ObserverWatchGainStateError>(
fuchsia_audio_device::ObserverWatchGainStateError::kWrongDeviceType));
return;
}
if (watch_gain_state_completer_) {
ADR_WARN_METHOD() << "previous `WatchGainState` request has not yet completed";
completer.Reply(fit::error<fuchsia_audio_device::ObserverWatchGainStateError>(
fuchsia_audio_device::ObserverWatchGainStateError::kAlreadyPending));
return;
}
watch_gain_state_completer_ = completer.ToAsync();
MaybeCompleteWatchGainState();
}
void ObserverServer::GainStateChanged(const fuchsia_audio_device::GainState& new_gain_state) {
ADR_LOG_METHOD(kLogObserverServerMethods || kLogNotifyMethods);
FX_DCHECK(device_->is_stream_config());
new_gain_state_to_notify_ = new_gain_state;
MaybeCompleteWatchGainState();
}
void ObserverServer::MaybeCompleteWatchGainState() {
if (watch_gain_state_completer_ && new_gain_state_to_notify_) {
auto completer = std::move(*watch_gain_state_completer_);
watch_gain_state_completer_.reset();
fuchsia_audio_device::ObserverWatchGainStateResponse response{
{.state = std::move(*new_gain_state_to_notify_)}};
new_gain_state_to_notify_.reset();
completer.Reply(fit::success(response));
}
}
void ObserverServer::WatchPlugState(WatchPlugStateCompleter::Sync& completer) {
ADR_LOG_METHOD(kLogObserverServerMethods);
if (device_has_error_) {
ADR_WARN_METHOD() << "Device encountered an error and will be removed";
completer.Reply(fit::error<fuchsia_audio_device::ObserverWatchPlugStateError>(
fuchsia_audio_device::ObserverWatchPlugStateError::kDeviceError));
return;
}
FX_CHECK(device_);
if (!device_->is_codec() && !device_->is_stream_config()) {
ADR_WARN_METHOD() << "This method is not supported for this device type";
completer.Reply(fit::error<fuchsia_audio_device::ObserverWatchPlugStateError>(
fuchsia_audio_device::ObserverWatchPlugStateError::kWrongDeviceType));
return;
}
if (watch_plug_state_completer_) {
ADR_WARN_METHOD() << "previous `WatchPlugState` request has not yet completed";
completer.Reply(fit::error<fuchsia_audio_device::ObserverWatchPlugStateError>(
fuchsia_audio_device::ObserverWatchPlugStateError::kAlreadyPending));
return;
}
watch_plug_state_completer_ = completer.ToAsync();
MaybeCompleteWatchPlugState();
}
void ObserverServer::PlugStateChanged(const fuchsia_audio_device::PlugState& new_plug_state,
zx::time plug_change_time) {
ADR_LOG_METHOD(kLogObserverServerMethods || kLogNotifyMethods)
<< new_plug_state << " @ " << plug_change_time.get();
new_plug_state_to_notify_ = fuchsia_audio_device::ObserverWatchPlugStateResponse{{
.state = new_plug_state,
.plug_time = plug_change_time.get(),
}};
MaybeCompleteWatchPlugState();
}
void ObserverServer::MaybeCompleteWatchPlugState() {
if (watch_plug_state_completer_ && new_plug_state_to_notify_) {
auto completer = std::move(*watch_plug_state_completer_);
watch_plug_state_completer_.reset();
auto new_plug_state = std::move(*new_plug_state_to_notify_);
new_plug_state_to_notify_.reset();
completer.Reply(fit::success(new_plug_state));
}
}
void ObserverServer::GetReferenceClock(GetReferenceClockCompleter::Sync& completer) {
ADR_LOG_METHOD(kLogObserverServerMethods);
if (device_has_error_) {
ADR_WARN_METHOD() << "Device encountered an error and will be removed";
completer.Reply(fit::error<fuchsia_audio_device::ObserverGetReferenceClockError>(
fuchsia_audio_device::ObserverGetReferenceClockError::kDeviceError));
return;
}
FX_CHECK(device_);
if (!device_->is_composite() && !device_->is_stream_config()) {
ADR_WARN_METHOD() << "This method is not supported for this device type";
completer.Reply(fit::error<fuchsia_audio_device::ObserverGetReferenceClockError>(
fuchsia_audio_device::ObserverGetReferenceClockError::kWrongDeviceType));
return;
}
auto clock_result = device_->GetReadOnlyClock();
if (clock_result.is_error()) {
ADR_WARN_METHOD() << "Device clock could not be created";
completer.Reply(fit::error<fuchsia_audio_device::ObserverGetReferenceClockError>(
fuchsia_audio_device::ObserverGetReferenceClockError::kDeviceClockUnavailable));
return;
}
fuchsia_audio_device::ObserverGetReferenceClockResponse response = {{
.reference_clock = std::move(clock_result.value()),
}};
completer.Reply(fit::success(std::move(response)));
}
void ObserverServer::GetElements(GetElementsCompleter::Sync& completer) {
ADR_LOG_METHOD(kLogObserverServerMethods);
if (device_has_error_) {
ADR_WARN_METHOD() << "Device has error";
completer.Reply(fit::error(ZX_ERR_INTERNAL));
return;
}
FX_CHECK(device_);
if (!device_->is_codec() && !device_->is_composite() && !device_->is_stream_config()) {
ADR_WARN_METHOD() << "This device_type does not support " << __func__;
completer.Reply(fit::error(ZX_ERR_WRONG_TYPE));
return;
}
if (!device_->supports_signalprocessing()) {
ADR_LOG_METHOD(kLogObserverServerMethods) << "This driver does not support signalprocessing";
completer.Reply(fit::error(ZX_ERR_NOT_SUPPORTED));
return;
}
FX_CHECK(device_->info().has_value() &&
device_->info()->signal_processing_elements().has_value() &&
!device_->info()->signal_processing_elements()->empty());
completer.Reply(fit::success(*device_->info()->signal_processing_elements()));
}
void ObserverServer::GetTopologies(GetTopologiesCompleter::Sync& completer) {
ADR_LOG_METHOD(kLogObserverServerMethods);
if (device_has_error_) {
ADR_WARN_METHOD() << "Device has error";
completer.Reply(fit::error(ZX_ERR_INTERNAL));
return;
}
FX_CHECK(device_);
if (!device_->is_codec() && !device_->is_composite() && !device_->is_stream_config()) {
ADR_WARN_METHOD() << "This device_type does not support " << __func__;
completer.Reply(fit::error(ZX_ERR_WRONG_TYPE));
return;
}
if (!device_->supports_signalprocessing()) {
ADR_LOG_METHOD(kLogObserverServerMethods) << "This driver does not support signalprocessing";
completer.Reply(fit::error(ZX_ERR_NOT_SUPPORTED));
return;
}
FX_CHECK(device_->info().has_value() &&
device_->info()->signal_processing_topologies().has_value() &&
!device_->info()->signal_processing_topologies()->empty());
completer.Reply(fit::success(*device_->info()->signal_processing_topologies()));
}
void ObserverServer::WatchTopology(WatchTopologyCompleter::Sync& completer) {
ADR_LOG_METHOD(kLogObserverServerMethods);
if (device_has_error_) {
ADR_WARN_METHOD() << "Device has error";
completer.Close(ZX_ERR_INTERNAL);
return;
}
FX_CHECK(device_);
if (!device_->is_codec() && !device_->is_composite() && !device_->is_stream_config()) {
ADR_WARN_METHOD() << "This device_type does not support " << __func__;
completer.Close(ZX_ERR_WRONG_TYPE);
return;
}
if (!device_->supports_signalprocessing()) {
ADR_WARN_METHOD() << "This driver does not support signalprocessing";
completer.Close(ZX_ERR_NOT_SUPPORTED);
return;
}
if (watch_topology_completer_) {
ADR_WARN_METHOD() << "previous `WatchTopology` request has not yet completed";
completer.Close(ZX_ERR_BAD_STATE);
return;
}
watch_topology_completer_ = completer.ToAsync();
MaybeCompleteWatchTopology();
}
void ObserverServer::TopologyChanged(TopologyId topology_id) {
ADR_LOG_METHOD(kLogObserverServerMethods || kLogNotifyMethods)
<< "(topology_id " << topology_id << ")";
topology_id_to_notify_ = topology_id;
MaybeCompleteWatchTopology();
}
void ObserverServer::MaybeCompleteWatchTopology() {
// ADR_LOG_METHOD(kLogObserverServerMethods || kLogNotifyMethods);
if (watch_topology_completer_.has_value() && topology_id_to_notify_.has_value()) {
ADR_LOG_METHOD(kLogObserverServerMethods || kLogNotifyMethods) << " will Reply";
auto completer = std::move(*watch_topology_completer_);
watch_topology_completer_.reset();
auto new_topology_id = *topology_id_to_notify_;
topology_id_to_notify_.reset();
completer.Reply(new_topology_id);
} else {
ADR_LOG_METHOD(kLogObserverServerMethods || kLogNotifyMethods) << " did not occur";
}
}
void ObserverServer::WatchElementState(WatchElementStateRequest& request,
WatchElementStateCompleter::Sync& completer) {
ADR_LOG_METHOD(kLogObserverServerMethods);
if (device_has_error_) {
ADR_WARN_METHOD() << "Device has error";
completer.Close(ZX_ERR_INTERNAL);
return;
}
FX_CHECK(device_);
if (!device_->is_codec() && !device_->is_composite() && !device_->is_stream_config()) {
ADR_WARN_METHOD() << "This device_type does not support " << __func__;
completer.Close(ZX_ERR_WRONG_TYPE);
return;
}
if (!device_->supports_signalprocessing()) {
ADR_WARN_METHOD() << "This driver does not support signalprocessing";
completer.Close(ZX_ERR_NOT_SUPPORTED);
return;
}
ElementId element_id = request.processing_element_id();
if (device_->element_ids().find(element_id) == device_->element_ids().end()) {
ADR_WARN_METHOD() << "unknown element_id " << element_id;
completer.Close(ZX_ERR_INVALID_ARGS);
return;
}
if (watch_element_state_completers_.find(element_id) != watch_element_state_completers_.end()) {
ADR_WARN_METHOD() << "previous `WatchElementState(" << element_id
<< ")` request has not yet completed";
completer.Close(ZX_ERR_BAD_STATE);
return;
}
watch_element_state_completers_.insert({element_id, completer.ToAsync()});
MaybeCompleteWatchElementState(element_id);
}
void ObserverServer::ElementStateChanged(
ElementId element_id, fuchsia_hardware_audio_signalprocessing::ElementState element_state) {
ADR_LOG_METHOD(kLogObserverServerMethods || kLogNotifyMethods)
<< "(element_id " << element_id << ")";
element_states_to_notify_.insert_or_assign(element_id, element_state);
MaybeCompleteWatchElementState(element_id);
}
// If we have an outstanding hanging-get and a state-change, respond with the state change.
void ObserverServer::MaybeCompleteWatchElementState(ElementId element_id) {
// ADR_LOG_METHOD(kLogObserverServerMethods || kLogNotifyMethods) << element_id;
if (watch_element_state_completers_.find(element_id) != watch_element_state_completers_.end() &&
element_states_to_notify_.find(element_id) != element_states_to_notify_.end()) {
ADR_LOG_METHOD(kLogObserverServerMethods || kLogNotifyMethods) << element_id << " will Reply";
auto completer = std::move(watch_element_state_completers_.find(element_id)->second);
watch_element_state_completers_.erase(element_id);
auto new_element_state = element_states_to_notify_.find(element_id)->second;
element_states_to_notify_.erase(element_id);
completer.Reply(new_element_state);
} else {
ADR_LOG_METHOD(kLogObserverServerMethods || kLogNotifyMethods) << " did not occur";
}
}
} // namespace media_audio