blob: 7864e6d6661571e0f2196f8100a6afd3b1556f33 [file] [log] [blame]
// Copyright 2024 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.
#ifndef SRC_MEDIA_AUDIO_SERVICES_DEVICE_REGISTRY_TESTING_FAKE_COMPOSITE_H_
#define SRC_MEDIA_AUDIO_SERVICES_DEVICE_REGISTRY_TESTING_FAKE_COMPOSITE_H_
#include <fidl/fuchsia.hardware.audio.signalprocessing/cpp/fidl.h>
#include <fidl/fuchsia.hardware.audio.signalprocessing/cpp/test_base.h>
#include <fidl/fuchsia.hardware.audio/cpp/fidl.h>
#include <fidl/fuchsia.hardware.audio/cpp/test_base.h>
#include <lib/fidl/cpp/unified_messaging_declarations.h>
#include <lib/fidl/cpp/wire/internal/transport_channel.h>
#include <lib/fit/result.h>
#include <lib/fzl/vmo-mapper.h>
#include <lib/zx/channel.h>
#include <lib/zx/clock.h>
#include <zircon/errors.h>
#include <zircon/time.h>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <optional>
#include <string_view>
#include "src/media/audio/services/device_registry/basic_types.h"
#include "src/media/audio/services/device_registry/logging.h"
#include "src/media/audio/services/device_registry/testing/fake_composite_ring_buffer.h"
namespace media_audio {
static constexpr bool kLogFakeComposite = false;
class FakeCompositeRingBuffer;
// This driver implements the audio driver interface and is configurable to simulate audio hardware.
using fuchsia_hardware_audio::Composite;
using fuchsia_hardware_audio::CompositeConnector;
using fuchsia_hardware_audio_signalprocessing::SignalProcessing;
class FakeComposite : public std::enable_shared_from_this<FakeComposite>,
public fidl::testing::TestBase<Composite>,
public fidl::testing::TestBase<SignalProcessing> {
public:
static constexpr char kDefaultManufacturer[] = "fake_composite device manufacturer";
static constexpr char kDefaultProduct[] = "fake_composite device product";
static constexpr UniqueId kDefaultUniqueInstanceId{
0xF1, 0xD3, 0xB5, 0x97, 0x79, 0x5B, 0x3D, 0x1F,
0x0E, 0x2C, 0x4A, 0x68, 0x86, 0xA4, 0xC2, 0xE0,
};
// DaiFormats and format sets
//
static constexpr uint32_t kDefaultDaiNumberOfChannels = 1;
static constexpr uint32_t kDefaultDaiNumberOfChannels2 = 2;
static constexpr fuchsia_hardware_audio::DaiSampleFormat kDefaultDaiSampleFormat =
fuchsia_hardware_audio::DaiSampleFormat::kPcmSigned;
static constexpr fuchsia_hardware_audio::DaiSampleFormat kDefaultDaiSampleFormat2 =
fuchsia_hardware_audio::DaiSampleFormat::kPcmFloat;
static constexpr uint32_t kDefaultDaiFrameRate = 48000;
static constexpr uint32_t kDefaultDaiFrameRate2 = 96000;
static constexpr uint8_t kDefaultDaiBitsPerSlot = 16;
static constexpr uint8_t kDefaultDaiBitsPerSlot2 = 32;
static constexpr uint8_t kDefaultDaiBitsPerSample = 16;
static constexpr uint8_t kDefaultDaiBitsPerSample2 = 32;
static const fuchsia_hardware_audio::DaiFrameFormat kDefaultDaiFrameFormat;
static const fuchsia_hardware_audio::DaiFrameFormat kDefaultDaiFrameFormat2;
static const std::vector<uint32_t> kDefaultDaiNumberOfChannelsSet;
static const std::vector<uint32_t> kDefaultDaiNumberOfChannelsSet2;
static const std::vector<fuchsia_hardware_audio::DaiSampleFormat> kDefaultDaiSampleFormatsSet;
static const std::vector<fuchsia_hardware_audio::DaiSampleFormat> kDefaultDaiSampleFormatsSet2;
static const std::vector<fuchsia_hardware_audio::DaiFrameFormat> kDefaultDaiFrameFormatsSet;
static const std::vector<fuchsia_hardware_audio::DaiFrameFormat> kDefaultDaiFrameFormatsSet2;
static const std::vector<uint32_t> kDefaultDaiFrameRates;
static const std::vector<uint32_t> kDefaultDaiFrameRates2;
static const std::vector<uint8_t> kDefaultDaiBitsPerSlotSet;
static const std::vector<uint8_t> kDefaultDaiBitsPerSlotSet2;
static const std::vector<uint8_t> kDefaultDaiBitsPerSampleSet;
static const std::vector<uint8_t> kDefaultDaiBitsPerSampleSet2;
static const fuchsia_hardware_audio::DaiSupportedFormats kDefaultDaiFormatSet;
static const fuchsia_hardware_audio::DaiSupportedFormats kDefaultDaiFormatSet2;
static const std::vector<fuchsia_hardware_audio::DaiSupportedFormats> kDefaultDaiFormatSets;
static const std::vector<fuchsia_hardware_audio::DaiSupportedFormats> kDefaultDaiFormatSets2;
static const std::unordered_map<ElementId,
std::vector<fuchsia_hardware_audio::DaiSupportedFormats>>
kDefaultDaiFormatsMap;
static const fuchsia_hardware_audio::DaiFormat kDefaultDaiFormat;
static const fuchsia_hardware_audio::DaiFormat kDefaultDaiFormat2;
// RingBufferFormats and format sets
//
static constexpr size_t kDefaultRingBufferAllocationSize = 8000;
static constexpr uint8_t kDefaultRbNumberOfChannels = 2;
static constexpr uint8_t kDefaultRbNumberOfChannels2 = 1;
static constexpr uint32_t kDefaultRbChannelAttributeMinFrequency = 50;
static constexpr uint32_t kDefaultRbChannelAttributeMinFrequency2 = 20000;
static constexpr uint32_t kDefaultRbChannelAttributeMaxFrequency = 22000;
static constexpr uint32_t kDefaultRbChannelAttributeMaxFrequency2 = 16000;
static const fuchsia_hardware_audio::ChannelAttributes kDefaultRbAttributes;
static const fuchsia_hardware_audio::ChannelAttributes kDefaultRbAttributes2;
static const fuchsia_hardware_audio::ChannelAttributes kDefaultRbAttributes3;
static const std::vector<fuchsia_hardware_audio::ChannelAttributes> kDefaultRbAttributeSet;
static const std::vector<fuchsia_hardware_audio::ChannelAttributes> kDefaultRbAttributeSet2;
static const fuchsia_hardware_audio::ChannelSet kDefaultRbChannelsSet;
static const fuchsia_hardware_audio::ChannelSet kDefaultRbChannelsSet2;
static const std::vector<fuchsia_hardware_audio::ChannelSet> kDefaultRbChannelsSets;
static const std::vector<fuchsia_hardware_audio::ChannelSet> kDefaultRbChannelsSets2;
static constexpr fuchsia_hardware_audio::SampleFormat kDefaultRbSampleFormat =
fuchsia_hardware_audio::SampleFormat::kPcmSigned;
static constexpr fuchsia_hardware_audio::SampleFormat kDefaultRbSampleFormat2 =
fuchsia_hardware_audio::SampleFormat::kPcmSigned;
static const std::vector<fuchsia_hardware_audio::SampleFormat> kDefaultRbSampleFormats;
static const std::vector<fuchsia_hardware_audio::SampleFormat> kDefaultRbSampleFormats2;
static constexpr uint8_t kDefaultRbBytesPerSample = 2;
static constexpr uint8_t kDefaultRbBytesPerSample2 = 4;
static const std::vector<uint8_t> kDefaultRbBytesPerSampleSet;
static const std::vector<uint8_t> kDefaultRbBytesPerSampleSet2;
static constexpr uint8_t kDefaultRbValidBitsPerSample = 16;
static constexpr uint8_t kDefaultRbValidBitsPerSample2 = 20;
static const std::vector<uint8_t> kDefaultRbValidBitsPerSampleSet;
static const std::vector<uint8_t> kDefaultRbValidBitsPerSampleSet2;
static constexpr uint32_t kDefaultRbFrameRate = 48000;
static constexpr uint32_t kDefaultRbFrameRate2 = 44100;
static const std::vector<uint32_t> kDefaultRbFrameRates;
static const std::vector<uint32_t> kDefaultRbFrameRates2;
static const fuchsia_hardware_audio::PcmSupportedFormats kDefaultPcmRingBufferFormatSet;
static const fuchsia_hardware_audio::PcmSupportedFormats kDefaultPcmRingBufferFormatSet2;
static const fuchsia_hardware_audio::SupportedFormats kDefaultRbFormatSet;
static const fuchsia_hardware_audio::SupportedFormats kDefaultRbFormatSet2;
static const std::vector<fuchsia_hardware_audio::SupportedFormats> kDefaultRbFormatSets;
static const std::vector<fuchsia_hardware_audio::SupportedFormats> kDefaultRbFormatSets2;
static const std::unordered_map<ElementId, std::vector<fuchsia_hardware_audio::SupportedFormats>>
kDefaultRbFormatsMap;
static const fuchsia_hardware_audio::PcmFormat kDefaultRbFormat;
static const fuchsia_hardware_audio::PcmFormat kDefaultRbFormat2;
// signalprocessing elements and topologies
//
// For min/max checks based on ranges, keep the DAI and RB element ID ranges contiguous.
static constexpr ElementId kSourceDaiElementId = 0;
static constexpr ElementId kDestDaiElementId = 1;
static constexpr ElementId kMinDaiElementId = kSourceDaiElementId;
static constexpr ElementId kMaxDaiElementId = kDestDaiElementId;
static constexpr ElementId kDestRbElementId = 2;
static constexpr ElementId kSourceRbElementId = 3;
static constexpr ElementId kMinRingBufferElementId = kDestRbElementId;
static constexpr ElementId kMaxRingBufferElementId = kSourceRbElementId;
static constexpr ElementId kMinElementId = kSourceDaiElementId;
static constexpr ElementId kMaxElementId = kSourceRbElementId;
static const fuchsia_hardware_audio_signalprocessing::Element kSourceDaiElement;
static const fuchsia_hardware_audio_signalprocessing::Element kDestRbElement;
static const fuchsia_hardware_audio_signalprocessing::Element kSourceRbElement;
static const fuchsia_hardware_audio_signalprocessing::Element kDestDaiElement;
static const fuchsia_hardware_audio_signalprocessing::Latency kSourceDaiElementLatency;
static const fuchsia_hardware_audio_signalprocessing::Latency kDestRbElementLatency;
static const fuchsia_hardware_audio_signalprocessing::Latency kSourceRbElementLatency;
static const fuchsia_hardware_audio_signalprocessing::Latency kDestDaiElementLatency;
static const fuchsia_hardware_audio_signalprocessing::ElementState kSourceDaiElementInitState;
static const fuchsia_hardware_audio_signalprocessing::ElementState kDestRbElementInitState;
static const fuchsia_hardware_audio_signalprocessing::ElementState kSourceRbElementInitState;
static const fuchsia_hardware_audio_signalprocessing::ElementState kDestDaiElementInitState;
static const std::vector<fuchsia_hardware_audio_signalprocessing::Element> kElements;
// For min/max checks based on ranges, keep this range contiguous.
static constexpr TopologyId kInputOnlyTopologyId = 10;
static constexpr TopologyId kFullDuplexTopologyId = 11;
static constexpr TopologyId kOutputOnlyTopologyId = 12;
static constexpr TopologyId kMinTopologyId = kInputOnlyTopologyId;
static constexpr TopologyId kMaxTopologyId = kOutputOnlyTopologyId;
static const fuchsia_hardware_audio_signalprocessing::EdgePair kTopologyInputEdgePair;
static const fuchsia_hardware_audio_signalprocessing::EdgePair kTopologyOutputEdgePair;
static const fuchsia_hardware_audio_signalprocessing::Topology kInputOnlyTopology;
static const fuchsia_hardware_audio_signalprocessing::Topology kFullDuplexTopology;
static const fuchsia_hardware_audio_signalprocessing::Topology kOutputOnlyTopology;
static const std::vector<fuchsia_hardware_audio_signalprocessing::Topology> kTopologies;
FakeComposite(zx::channel server_end, zx::channel client_end, async_dispatcher_t* dispatcher);
void NotImplemented_(const std::string& name, ::fidl::CompleterBase& completer) override {
ADR_WARN_OBJECT() << name;
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
// This returns a fidl::client_end<Composite>. The driver will not start serving requests until
// Enable is called, which is why the construction/Enable separation exists.
fidl::ClientEnd<fuchsia_hardware_audio::Composite> Enable();
void DropComposite();
void DropChildren();
void DropRingBuffers();
void DropRingBuffer(ElementId element_id);
static void on_rb_unbind(FakeCompositeRingBuffer* fake_ring_buffer, fidl::UnbindInfo info,
fidl::ServerEnd<fuchsia_hardware_audio::RingBuffer>);
void RingBufferWasDropped(ElementId element_id);
// These may be called before the RingBuffer object is created; info must be cached until then.
void ReserveRingBufferSize(ElementId element_id, size_t size);
void EnableActiveChannelsSupport(ElementId element_id);
void DisableActiveChannelsSupport(ElementId element_id);
void PresetTurnOnDelay(ElementId element_id, std::optional<zx::duration> turn_on_delay);
void PresetInternalExternalDelays(ElementId element_id, zx::duration internal_delay,
std::optional<zx::duration> external_delay);
async_dispatcher_t* dispatcher() { return dispatcher_; }
bool is_bound() const { return binding_.has_value(); }
bool responsive() const { return responsive_; }
void set_responsive(bool responsive) { responsive_ = responsive; }
std::optional<bool> health_state() const { return healthy_; }
void set_health_state(std::optional<bool> healthy) { healthy_ = healthy; }
void set_device_manufacturer(std::optional<std::string> mfgr) { manufacturer_ = std::move(mfgr); }
void set_device_product(std::optional<std::string> product) { product_ = std::move(product); }
void set_stream_unique_id(std::optional<UniqueId> uid) {
if (uid) {
std::memcpy(uid_->data(), uid->data(), sizeof(*uid));
} else {
uid_.reset();
}
}
void set_clock_domain(std::optional<ClockDomain> clock_domain) { clock_domain_ = clock_domain; }
bool is_ring_buffer(ElementId element_id) const {
for (auto& element_iter : elements_) {
if (element_iter.first == element_id) {
return (element_iter.second.element.type() ==
fuchsia_hardware_audio_signalprocessing::ElementType::kEndpoint &&
element_iter.second.element.type_specific()->endpoint()->type() ==
fuchsia_hardware_audio_signalprocessing::EndpointType::kRingBuffer);
}
}
return false; // We didn't find the element.
}
// These rely on the RingBuffer being created; do not use them to pre-configure the RingBuffer.
uint64_t active_channels_bitmask(ElementId element_id) const {
FX_CHECK(is_ring_buffer(element_id));
return ring_buffers_.find(element_id)->second->active_channels_bitmask();
}
zx::time active_channels_set_time(ElementId element_id) const {
FX_CHECK(is_ring_buffer(element_id));
return ring_buffers_.find(element_id)->second->active_channels_set_time();
}
bool started(ElementId element_id) const {
FX_CHECK(is_ring_buffer(element_id));
return ring_buffers_.find(element_id)->second->started();
}
zx::time mono_start_time(ElementId element_id) const {
FX_CHECK(is_ring_buffer(element_id));
return ring_buffers_.find(element_id)->second->mono_start_time();
}
// Explicitly trigger a change notification, for the current values of gain/plug/delay.
void InjectDelayUpdate(ElementId element_id, std::optional<zx::duration> internal_delay,
std::optional<zx::duration> external_delay) {
FX_CHECK(is_ring_buffer(element_id));
ring_buffers_.find(element_id)->second->InjectDelayUpdate(internal_delay, external_delay);
}
void InjectTopologyChange(std::optional<TopologyId> topology_id);
void InjectElementStateChange(ElementId element_id,
fuchsia_hardware_audio_signalprocessing::ElementState new_state);
private:
friend FakeCompositeRingBuffer;
static inline const std::string_view kClassName = "FakeComposite";
struct FakeElementRecord {
fuchsia_hardware_audio_signalprocessing::Element element;
fuchsia_hardware_audio_signalprocessing::ElementState state;
bool state_has_changed = true; // immediately complete the first WatchElementState request
std::optional<WatchElementStateCompleter::Async> watch_completer;
};
void SetupElementsMap();
// fuchsia_hardware_audio::Composite implementation
void Reset(ResetCompleter::Sync& completer) override;
void GetProperties(GetPropertiesCompleter::Sync& completer) override;
void GetRingBufferFormats(GetRingBufferFormatsRequest& request,
GetRingBufferFormatsCompleter::Sync& completer) override;
void CreateRingBuffer(CreateRingBufferRequest& request,
CreateRingBufferCompleter::Sync& completer) override;
void GetDaiFormats(GetDaiFormatsRequest& request,
GetDaiFormatsCompleter::Sync& completer) override;
void SetDaiFormat(SetDaiFormatRequest& request, SetDaiFormatCompleter::Sync& completer) override;
// fuchsia.hardware.audio.Health implementation
void GetHealthState(GetHealthStateCompleter::Sync& completer) override;
// fuchsia.hardware.audio.signalprocessing.Connector implementation
void SignalProcessingConnect(SignalProcessingConnectRequest& request,
SignalProcessingConnectCompleter::Sync& completer) override;
// fuchsia.hardware.audio.signalprocessing.SignalProcessing implementation (including Reader)
void GetElements(GetElementsCompleter::Sync& completer) final;
void GetTopologies(GetTopologiesCompleter::Sync& completer) final;
void WatchElementState(WatchElementStateRequest& request,
WatchElementStateCompleter::Sync& completer) final;
void WatchTopology(WatchTopologyCompleter::Sync& completer) final;
void SetElementState(SetElementStateRequest& request,
SetElementStateCompleter::Sync& completer) final;
void SetTopology(SetTopologyRequest& request, SetTopologyCompleter::Sync& completer) final;
// Internal implementation methods/members
static bool DaiFormatIsSupported(ElementId element_id,
const fuchsia_hardware_audio::DaiFormat& format);
static void CheckForElementStateCompletion(FakeElementRecord& element_record);
void CheckForTopologyCompletion();
async_dispatcher_t* dispatcher_;
fidl::ServerEnd<fuchsia_hardware_audio::Composite> server_end_;
fidl::ClientEnd<fuchsia_hardware_audio::Composite> client_end_;
std::optional<fidl::ServerBindingRef<Composite>> binding_;
bool responsive_ = true;
std::optional<bool> healthy_ = true;
std::optional<GetHealthStateCompleter::Async> health_completer_;
std::optional<std::string> manufacturer_ = kDefaultManufacturer;
std::optional<std::string> product_ = kDefaultProduct;
std::optional<UniqueId> uid_ = kDefaultUniqueInstanceId;
std::optional<ClockDomain> clock_domain_ = fuchsia_hardware_audio::kClockDomainMonotonic;
bool supports_signalprocessing_ = true;
std::optional<fidl::ServerBindingRef<SignalProcessing>> signal_processing_binding_;
std::unordered_map<ElementId, FakeElementRecord> elements_;
std::optional<WatchTopologyCompleter::Async> watch_topology_completer_;
std::optional<TopologyId> topology_id_ = kFullDuplexTopologyId;
bool topology_has_changed_ = true;
std::unordered_map<ElementId, size_t> ring_buffer_allocation_sizes_;
std::unordered_map<ElementId, bool> active_channels_support_overrides_;
std::unordered_map<ElementId, std::optional<zx::duration>> turn_on_delay_overrides_;
std::unordered_map<ElementId, zx::duration> internal_delay_overrides_;
std::unordered_map<ElementId, std::optional<zx::duration>> external_delay_overrides_;
std::unordered_map<ElementId, fidl::ServerBindingRef<fuchsia_hardware_audio::RingBuffer>>
ring_buffer_bindings_;
std::unordered_map<ElementId, std::unique_ptr<FakeCompositeRingBuffer>> ring_buffers_;
};
} // namespace media_audio
#endif // SRC_MEDIA_AUDIO_SERVICES_DEVICE_REGISTRY_TESTING_FAKE_COMPOSITE_H_