blob: 23b0908c45a4a11f817e2321eaa350d812b0f1e8 [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_CODEC_H_
#define SRC_MEDIA_AUDIO_SERVICES_DEVICE_REGISTRY_TESTING_FAKE_CODEC_H_
#include <fidl/fuchsia.audio.device/cpp/fidl.h>
#include <fidl/fuchsia.hardware.audio.signalprocessing/cpp/markers.h>
#include <fidl/fuchsia.hardware.audio.signalprocessing/cpp/test_base.h>
#include <fidl/fuchsia.hardware.audio/cpp/natural_types.h>
#include <fidl/fuchsia.hardware.audio/cpp/test_base.h>
#include <lib/async/cpp/time.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/fpromise/result.h>
#include <lib/fzl/vmo-mapper.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/channel.h>
#include <lib/zx/time.h>
#include <lib/zx/vmo.h>
#include <zircon/errors.h>
#include <cstring>
#include <optional>
#include <string_view>
#include "src/media/audio/services/device_registry/basic_types.h"
#include "src/media/audio/services/device_registry/logging.h"
namespace media_audio {
inline constexpr bool kLogFakeCodec = false;
// This driver implements the audio driver interface and is configurable to simulate audio hardware.
using fuchsia_hardware_audio::Codec;
using fuchsia_hardware_audio::CodecConnector;
using fuchsia_hardware_audio_signalprocessing::SignalProcessing;
class FakeCodec : public fidl::testing::TestBase<CodecConnector>,
public fidl::testing::TestBase<Codec>,
public fidl::testing::TestBase<SignalProcessing> {
public:
static constexpr char kDefaultManufacturer[] = "fake_codec device manufacturer";
static constexpr char kDefaultProduct[] = "fake_codec device product";
static constexpr UniqueId kDefaultUniqueInstanceId{
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
};
static constexpr bool kDefaultIsInput = false;
static constexpr uint32_t kDefaultNumberOfChannels = 1;
static constexpr uint32_t kDefaultNumberOfChannels2 = 2;
static constexpr fuchsia_hardware_audio::DaiSampleFormat kDefaultDaiSampleFormat =
fuchsia_hardware_audio::DaiSampleFormat::kPcmSigned;
static constexpr uint32_t kDefaultFrameRates = 48000;
static constexpr uint8_t kDefaultBitsPerSlot = 32;
static constexpr uint8_t kDefaultBitsPerSample = 16;
static constexpr fuchsia_hardware_audio::PlugDetectCapabilities kDefaultDriverPlugCaps =
fuchsia_hardware_audio::PlugDetectCapabilities::kCanAsyncNotify;
static constexpr fuchsia_audio_device::PlugDetectCapabilities kDefaultPlugCaps =
fuchsia_audio_device::PlugDetectCapabilities::kPluggable;
static const fuchsia_hardware_audio::DaiFrameFormat kDefaultFrameFormat;
static const std::vector<uint32_t> kDefaultNumberOfChannelsSet;
static const std::vector<fuchsia_hardware_audio::DaiSampleFormat> kDefaultSampleFormatsSet;
static const std::vector<fuchsia_hardware_audio::DaiFrameFormat> kDefaultFrameFormatsSet;
static const std::vector<uint32_t> kDefaultFrameRatesSet;
static const std::vector<uint8_t> kDefaultBitsPerSlotSet;
static const std::vector<uint8_t> kDefaultBitsPerSampleSet;
static const fuchsia_hardware_audio::DaiSupportedFormats kDefaultDaiFormatSet;
static const std::vector<fuchsia_hardware_audio::DaiSupportedFormats> kDefaultDaiFormatSets;
FakeCodec(zx::channel server_end, zx::channel client_end, async_dispatcher_t* dispatcher);
~FakeCodec() override;
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<Codec>. The driver will not start serving requests until
// Enable is called, which is why the construction/Enable separation exists.
fidl::ClientEnd<fuchsia_hardware_audio::Codec> Enable();
void DropCodec();
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; }
std::optional<bool> is_input() const { return is_input_; }
void set_is_input(std::optional<bool> is_input) { is_input_ = is_input; }
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) {
uid_ = UniqueId{};
std::memcpy(uid_->data(), uid->data(), fuchsia_audio_device::kUniqueInstanceIdSize);
} else {
uid_.reset();
}
}
void set_plug_detect_capabilities(
std::optional<fuchsia_hardware_audio::PlugDetectCapabilities> plug_detect_capabilities) {
plug_detect_capabilities_ = plug_detect_capabilities;
}
// By default, support 1 format set, limited to 2-channel, unsigned int 16-in-32, 48kHz, I2S.
void SetDefaultFormatSets() {
clear_format_sets();
number_of_channels_ = kDefaultNumberOfChannelsSet;
sample_formats_ = kDefaultSampleFormatsSet;
frame_formats_ = kDefaultFrameFormatsSet;
frame_rates_ = kDefaultFrameRatesSet;
bits_per_slot_ = kDefaultBitsPerSlotSet;
bits_per_sample_ = kDefaultBitsPerSampleSet;
format_sets_ = kDefaultDaiFormatSets;
}
void clear_format_sets() { format_sets_.clear(); }
void add_format_set(fuchsia_hardware_audio::DaiSupportedFormats format_set) {
format_sets_.push_back(std::move(format_set));
}
bool is_running() const { return is_running_; }
zx::time mono_start_time() const { return mono_start_time_; }
zx::time mono_stop_time() const { return mono_stop_time_; }
// The returned optional will be empty if no |SetDaiFormat| command has been received, or if the
// Codec's state has been reset.
std::optional<fuchsia_hardware_audio::DaiFormat> selected_format() const {
return selected_format_;
}
void set_external_delay(zx::duration external_delay) { external_delay_ = external_delay; }
void clear_external_delay() { external_delay_.reset(); }
void set_turn_on_delay(zx::duration turn_on_delay) { turn_on_delay_ = turn_on_delay; }
void clear_turn_on_delay() { turn_on_delay_.reset(); }
void set_turn_off_delay(zx::duration turn_off_delay) { turn_off_delay_ = turn_off_delay; }
void clear_turn_off_delay() { turn_off_delay_.reset(); }
// Explicitly trigger a plug change.
void InjectPluggedAt(zx::time plug_time);
void InjectUnpluggedAt(zx::time plug_time);
void HandlePlugResponse();
////////////////////////////////////////////////////////////////
private:
static inline const std::string_view kClassName = "FakeCodec";
// fuchsia_hardware_audio::CodecConnector
void Connect(ConnectRequest& request, ConnectCompleter::Sync& completer) override {
FX_CHECK(!binding_) << "Codec is already bound; it cannot have multiple clients";
binding_ = fidl::BindServer(dispatcher(), std::move(request.codec_protocol()), this);
}
// fuchsia_hardware_audio::Codec Interface
void Reset(ResetCompleter::Sync& completer) override;
void GetProperties(GetPropertiesCompleter::Sync& completer) override;
void Stop(StopCompleter::Sync& completer) override;
void Start(StartCompleter::Sync& completer) override;
void GetDaiFormats(GetDaiFormatsCompleter::Sync& completer) override;
void SetDaiFormat(SetDaiFormatRequest& request, SetDaiFormatCompleter::Sync& completer) override;
void WatchPlugState(WatchPlugStateCompleter::Sync& completer) override;
// These methods are deprecated and will be removed soon.
void IsBridgeable(IsBridgeableCompleter::Sync& completer) override {
ADR_LOG_OBJECT(kLogFakeCodec);
completer.Reply(false);
}
void SetBridgedMode(SetBridgedModeRequest& request,
SetBridgedModeCompleter::Sync& completer) override {
ADR_LOG_OBJECT(kLogFakeCodec);
}
// fuchsia.hardware.audio.Health
void GetHealthState(GetHealthStateCompleter::Sync& completer) override;
// fuchsia.hardware.audio.signalprocessing.Connector
void SignalProcessingConnect(SignalProcessingConnectRequest& request,
SignalProcessingConnectCompleter::Sync& completer) override;
// fuchsia.hardware.audio.signalprocessing.SignalProcessing
// These won't be called until SignalProcessingConnect is implemented, but we'll be safe.
// TODO(https://fxbug.dev/323270827): implement signalprocessing for Codec (topology, gain).
void GetElements(GetElementsCompleter::Sync& completer) final {
ADR_LOG_OBJECT(kLogFakeCodec);
completer.Reply(fit::error(ZX_ERR_NOT_SUPPORTED));
}
void WatchElementState(WatchElementStateRequest& request,
WatchElementStateCompleter::Sync& completer) final {
ADR_LOG_OBJECT(kLogFakeCodec);
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void GetTopologies(GetTopologiesCompleter::Sync& completer) final {
ADR_LOG_OBJECT(kLogFakeCodec);
completer.Reply(fit::error(ZX_ERR_NOT_SUPPORTED));
}
void WatchTopology(WatchTopologyCompleter::Sync& completer) final {
ADR_LOG_OBJECT(kLogFakeCodec);
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void SetElementState(SetElementStateRequest& request,
SetElementStateCompleter::Sync& completer) final {
ADR_LOG_OBJECT(kLogFakeCodec);
completer.Reply(fit::error(ZX_ERR_NOT_SUPPORTED));
}
void SetTopology(SetTopologyRequest& request, SetTopologyCompleter::Sync& completer) final {
ADR_LOG_OBJECT(kLogFakeCodec);
completer.Reply(fit::error(ZX_ERR_NOT_SUPPORTED));
}
bool CheckDaiFormatSupported(const fuchsia_hardware_audio::DaiFormat& candidate);
async_dispatcher_t* dispatcher_;
fidl::ServerEnd<fuchsia_hardware_audio::Codec> server_end_;
fidl::ClientEnd<fuchsia_hardware_audio::Codec> client_end_;
std::optional<fidl::ServerBindingRef<Codec>> binding_;
std::optional<fidl::ServerBindingRef<fuchsia_hardware_audio_signalprocessing::SignalProcessing>>
signal_processing_binding_;
bool responsive_ = true;
std::optional<bool> healthy_ = true;
std::optional<GetHealthStateCompleter::Async> health_completer_;
bool supports_signalprocessing_ = false;
std::optional<std::string> manufacturer_ = kDefaultManufacturer;
std::optional<std::string> product_ = kDefaultProduct;
std::optional<UniqueId> uid_ = kDefaultUniqueInstanceId;
std::optional<bool> is_input_ = kDefaultIsInput;
std::optional<fuchsia_hardware_audio::PlugDetectCapabilities> plug_detect_capabilities_ =
kDefaultDriverPlugCaps;
bool is_running_ = false;
zx::time mono_start_time_{zx::time::infinite_past()};
zx::time mono_stop_time_{zx::time::infinite_past()};
// Format sets
std::vector<fuchsia_hardware_audio::DaiSupportedFormats> format_sets_ = kDefaultDaiFormatSets;
// The default values for these six vectors are set by SetDefaultFormats(), in the ctor.
std::vector<uint32_t> number_of_channels_;
std::vector<fuchsia_hardware_audio::DaiSampleFormat> sample_formats_;
std::vector<fuchsia_hardware_audio::DaiFrameFormat> frame_formats_;
std::vector<uint32_t> frame_rates_;
std::vector<uint8_t> bits_per_slot_;
std::vector<uint8_t> bits_per_sample_;
// Current format
std::optional<fuchsia_hardware_audio::DaiFormat> selected_format_;
// CodecFormatInfo, returned when the format is set
std::optional<zx::duration> external_delay_;
std::optional<zx::duration> turn_on_delay_;
std::optional<zx::duration> turn_off_delay_;
// Plug state
// Always respond to the first hanging-get request.
bool plug_has_changed_ = true;
std::optional<WatchPlugStateCompleter::Async> watch_plug_state_completer_;
bool plugged_ = true;
zx::time plug_state_time_{zx::time::infinite_past()};
};
} // namespace media_audio
#endif // SRC_MEDIA_AUDIO_SERVICES_DEVICE_REGISTRY_TESTING_FAKE_CODEC_H_