blob: 04d42ab43fb68544877bf5c0068f1ec046708260 [file] [log] [blame]
// Copyright 2026 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/graphics/display/drivers/coordinator/client-set.h"
#include <fidl/fuchsia.hardware.display/cpp/wire.h>
#include <lib/driver/logging/cpp/logger.h>
#include <lib/inspect/cpp/vmo/types.h>
#include <lib/zx/result.h>
#include <lib/zx/time.h>
#include <zircon/status.h>
#include <zircon/time.h>
#include <memory>
#include <span>
#include <fbl/string_printf.h>
#include "src/graphics/display/drivers/coordinator/client.h"
#include "src/graphics/display/drivers/coordinator/controller.h"
#include "src/graphics/display/lib/api-types/cpp/client-priority.h"
#include "src/graphics/display/lib/api-types/cpp/display-id.h"
#include "src/graphics/display/lib/api-types/cpp/driver-config-stamp.h"
namespace display_coordinator {
ClientSet::ClientSet(inspect::Node root_node) : root_node_(std::move(root_node)) {}
ClientSet::~ClientSet() {
ZX_DEBUG_ASSERT_MSG(clients_.empty(), "Clear() must be called before the ClientSet is destroyed");
}
void ClientSet::DispatchOnDisplaysChanged(std::span<const display::DisplayId> added_display_ids,
std::span<const display::DisplayId> removed_display_ids) {
for (std::unique_ptr<Client>& client : clients_) {
client->OnDisplaysChanged(added_display_ids, removed_display_ids);
}
}
void ClientSet::DispatchOnDisplayVsync(display::DisplayId display_id, zx::time_monotonic timestamp,
display::DriverConfigStamp vsync_config_stamp,
display::ClientPriority client_priority) {
ZX_DEBUG_ASSERT(display_id != display::kInvalidDisplayId);
ZX_DEBUG_ASSERT(vsync_config_stamp != display::kInvalidDriverConfigStamp);
ZX_DEBUG_ASSERT(client_priority != display::ClientPriority::kInvalid);
zx_instant_mono_t fidl_timestamp = timestamp.get();
for (std::unique_ptr<Client>& client : clients_) {
if (client->priority() == client_priority) {
client->OnDisplayVsync(display_id, fidl_timestamp, vsync_config_stamp);
break;
}
}
}
void ClientSet::DispatchOnCaptureComplete() {
for (std::unique_ptr<Client>& client : clients_) {
client->OnCaptureComplete();
}
}
namespace {
void PrintChannelKoids(display::ClientPriority client_priority, const zx::channel& channel) {
ZX_DEBUG_ASSERT(client_priority != display::ClientPriority::kInvalid);
zx_info_handle_basic_t info{};
size_t actual, avail;
zx_status_t status = channel.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), &actual, &avail);
if (status != ZX_OK || info.type != ZX_OBJ_TYPE_CHANNEL) {
fdf::error("Failed to get koids for handle type {}: {}", info.type, status);
return;
}
ZX_DEBUG_ASSERT(actual == avail);
fdf::info("Client connecting at priority {} - FIDL client end: 0x{:x} server end: 0x{:x}",
client_priority, info.related_koid, info.koid);
}
} // namespace
zx::result<> ClientSet::ConnectClient(
Controller* controller, display::ClientPriority client_priority,
std::span<const display::DisplayId> current_display_ids,
fidl::ServerEnd<fuchsia_hardware_display::Coordinator> coordinator_server_end,
fidl::ClientEnd<fuchsia_hardware_display::CoordinatorListener>
coordinator_listener_client_end) {
ZX_DEBUG_ASSERT(controller != nullptr);
ZX_DEBUG_ASSERT(client_priority != display::ClientPriority::kInvalid);
ZX_DEBUG_ASSERT(coordinator_server_end.is_valid());
ZX_DEBUG_ASSERT(coordinator_listener_client_end.is_valid());
PrintChannelKoids(client_priority, coordinator_server_end.channel());
for (const std::unique_ptr<Client>& existing_client : clients_) {
if (existing_client->priority() == client_priority) {
fdf::warn("Rejecting client connection. A client is already bound at priority {}",
client_priority);
return zx::error(ZX_ERR_ALREADY_BOUND);
}
}
ClientId client_id = next_client_id_;
++next_client_id_;
fbl::AllocChecker alloc_checker;
auto client = fbl::make_unique_checked<Client>(&alloc_checker, controller, client_priority,
client_id, std::move(coordinator_server_end),
std::move(coordinator_listener_client_end));
if (!alloc_checker.check()) {
fdf::error("Failed to allocate memory for Client");
return zx::error(ZX_ERR_NO_MEMORY);
}
inspect::Node client_inspect_node =
root_node_.CreateChild(fbl::StringPrintf("client-%" PRIu64, client_id.value()));
client->AttachInspectNode(std::move(client_inspect_node));
Client* client_ptr = client.get();
clients_.push_back(std::move(client));
std::span<const display::DisplayId> removed_display_ids = {};
client_ptr->OnDisplaysChanged(current_display_ids, removed_display_ids);
fdf::info("Client connected at priority {} with ID {}", client_priority,
client_ptr->id().value());
HandleClientOwnershipChanges();
return zx::ok();
}
std::optional<display::ClientPriority> ClientSet::FindConfigStampSource(
display::DriverConfigStamp driver_config_stamp) {
for (const std::unique_ptr<Client>& client : clients_) {
const std::list<Client::ConfigStampPair>& pending_stamps =
client->pending_displayed_config_stamps();
auto pending_stamps_it =
std::ranges::find_if(pending_stamps, [&](const Client::ConfigStampPair& pending_stamp) {
return pending_stamp.driver_stamp >= driver_config_stamp;
});
if (pending_stamps_it == pending_stamps.end()) {
continue;
}
if (pending_stamps_it->driver_stamp == driver_config_stamp) {
return std::make_optional(client->priority());
}
}
return std::nullopt;
}
void ClientSet::OnClientDisconnected(Client* client) {
ZX_DEBUG_ASSERT(client != nullptr);
fdf::info("Client disconnected: priority {}, ID {}", client->priority(), client->id().value());
if (client == client_owning_displays_) {
client_owning_displays_ = nullptr;
}
for (auto clients_it = clients_.begin(); clients_it != clients_.end(); ++clients_it) {
if (clients_it->get() == client) {
clients_it->get()->ReleaseResources();
clients_.erase(clients_it);
break;
}
}
HandleClientOwnershipChanges();
}
void ClientSet::HandleClientOwnershipChanges() {
Client* new_client_owning_displays = nullptr;
for (std::unique_ptr<Client>& client : clients_) {
if (!new_client_owning_displays ||
client->priority() > new_client_owning_displays->priority()) {
new_client_owning_displays = client.get();
}
}
if (new_client_owning_displays != client_owning_displays_) {
if (client_owning_displays_) {
client_owning_displays_->SetOwnership(false);
}
if (new_client_owning_displays) {
new_client_owning_displays->SetOwnership(true);
}
client_owning_displays_ = new_client_owning_displays;
}
}
void ClientSet::Clear() {
for (const std::unique_ptr<Client>& client : clients_) {
client->CloseFidlConnection(ZX_ERR_CONNECTION_ABORTED);
client->ReleaseResources();
}
client_owning_displays_ = nullptr;
clients_.clear();
}
Client* ClientSet::GetClientOwningDisplays() const { return client_owning_displays_; }
} // namespace display_coordinator