blob: 5369922c1f6b9f5ce011a58b2f0b576aa22343ee [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/devices/bin/driver_manager/driver_development/driver_development_service.h"
#include <fidl/fuchsia.driver.framework/cpp/fidl.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <queue>
#include <unordered_set>
#include "src/devices/bin/driver_manager/driver_development/info_iterator.h"
#include "src/devices/bin/driver_manager/node_property_conversion.h"
#include "src/devices/lib/log/log.h"
namespace fdd = fuchsia_driver_development;
namespace fdf {
using namespace fuchsia_driver_framework;
}
namespace driver_manager {
namespace {
void SetNodeInfoBuilderNodeProperties(
fidl::AnyArena& allocator, const driver_manager::Node& node,
fidl::WireTableBuilder<fuchsia_driver_development::wire::NodeInfo> node_info_builder) {
// Composite node's "default" node properties are its primary parent's node properties which
// should not be used.
if (node.type() == driver_manager::NodeType::kComposite) {
return;
}
auto properties = node.GetNodeProperties();
// Avoid allocating an empty VectorView.
if (!properties.has_value() || properties->empty()) {
return;
}
fidl::VectorView<fdf::wire::NodeProperty> node_properties(allocator, properties->size());
for (size_t i = 0; i < properties->size(); ++i) {
node_properties[i] = ToDeprecatedProperty(allocator, properties.value()[i]);
}
node_info_builder.node_property_list(node_properties);
}
zx::result<fdd::wire::NodeInfo> CreateDeviceInfo(fidl::AnyArena& allocator,
const driver_manager::Node* node) {
auto device_info = fdd::wire::NodeInfo::Builder(allocator);
device_info.id(reinterpret_cast<uint64_t>(node));
const auto& children = node->children();
fidl::VectorView<uint64_t> child_ids(allocator, children.size());
size_t i = 0;
for (const auto& child : children) {
child_ids[i++] = reinterpret_cast<uint64_t>(child.get());
}
if (!child_ids.empty()) {
device_info.child_ids(child_ids);
}
const auto& parents = node->parents();
fidl::VectorView<uint64_t> parent_ids(allocator, parents.size());
i = 0;
for (const auto& parent : parents) {
auto parent_ptr = parent.lock();
if (!parent_ptr) {
fdf_log::error("Parent node freed before it could be used");
return zx::error(ZX_ERR_INTERNAL);
}
parent_ids[i++] = reinterpret_cast<uint64_t>(parent_ptr.get());
}
if (!parent_ids.empty()) {
device_info.parent_ids(parent_ids);
}
device_info.moniker(fidl::StringView(allocator, node->MakeComponentMoniker()));
device_info.quarantined(node->quarantined());
device_info.bound_driver_url(fidl::StringView(allocator, node->driver_url()));
SetNodeInfoBuilderNodeProperties(allocator, *node, device_info);
// TODO(https://fxbug.dev/42172220): Get topological path
auto driver_host = node->driver_host();
if (node->is_bound() && driver_host) {
auto result = driver_host->GetProcessKoid();
if (result.is_error()) {
// ZX_ERR_SHOULD_WAIT means the driver host hasn't been fully connected to yet.
// ZX_ERR_PEER_CLOSED means the host closed while we tried to query it.
if (result.error_value() != ZX_ERR_SHOULD_WAIT &&
result.error_value() != ZX_ERR_PEER_CLOSED) {
fdf_log::error("Failed to get the process KOID of a driver host: {}",
zx_status_get_string(result.status_value()));
return zx::error(result.status_value());
}
} else {
device_info.driver_host_koid(result.value());
}
}
// Copy over the offers.
const std::vector<NodeOffer>& offers = node->offers();
if (!offers.empty()) {
fidl::VectorView<fuchsia_component_decl::wire::Offer> node_offers(allocator, offers.size());
for (size_t i = 0; i < offers.size(); i++) {
const NodeOffer& offer = offers[i];
switch (offer.transport) {
case OfferTransport::DriverTransport:
node_offers[i] = fidl::ToWire(allocator, ToFidl(offer).driver_transport().value());
break;
case OfferTransport::ZirconTransport:
node_offers[i] = fidl::ToWire(allocator, ToFidl(offer).zircon_transport().value());
break;
case OfferTransport::Dictionary:
node_offers[i] = fidl::ToWire(allocator, ToFidl(offer).dictionary_offer().value());
break;
}
}
device_info.offer_list(node_offers);
}
std::vector topo_path = node->GetBusTopology();
device_info.bus_topology(fidl::ToWire(allocator, topo_path));
return zx::ok(device_info.Build());
}
} // namespace
DriverDevelopmentService::DriverDevelopmentService(driver_manager::DriverRunner& driver_runner,
async_dispatcher_t* dispatcher)
: driver_runner_(driver_runner), dispatcher_(dispatcher) {}
void DriverDevelopmentService::Publish(component::OutgoingDirectory& outgoing) {
auto result = outgoing.AddUnmanagedProtocol<fdd::Manager>(
bindings_.CreateHandler(this, dispatcher_, [](fidl::UnbindInfo info) {
if (!info.is_peer_closed()) {
fdf_log::warn("development service closed. {}", info.FormatDescription());
}
}));
ZX_ASSERT(result.is_ok());
}
void DriverDevelopmentService::GetNodeInfo(GetNodeInfoRequestView request,
GetNodeInfoCompleter::Sync& completer) {
auto arena = std::make_unique<fidl::Arena<512>>();
std::vector<fdd::wire::NodeInfo> device_infos;
std::unordered_set<const driver_manager::Node*> unique_nodes;
std::queue<const driver_manager::Node*> remaining_nodes;
remaining_nodes.push(driver_runner_.root_node().get());
while (!remaining_nodes.empty()) {
auto node = remaining_nodes.front();
remaining_nodes.pop();
auto [_, inserted] = unique_nodes.insert(node);
if (!inserted) {
// Only insert unique nodes from the DAG.
continue;
}
const auto& children = node->children();
for (const auto& child : children) {
remaining_nodes.push(child.get());
}
std::string moniker = node->MakeComponentMoniker();
if (!request->node_filter.empty()) {
bool found = false;
for (const auto& node_filter : request->node_filter) {
if (request->exact_match) {
if (moniker == node_filter.get()) {
found = true;
break;
}
} else {
if (moniker.find(node_filter.get()) != std::string::npos) {
found = true;
break;
}
}
}
if (!found) {
continue;
}
}
auto result = CreateDeviceInfo(*arena, node);
if (result.is_error()) {
return;
}
device_infos.push_back(std::move(result.value()));
}
auto iterator = std::make_unique<driver_development::DeviceInfoIterator>(std::move(arena),
std::move(device_infos));
fidl::BindServer(
this->dispatcher_, std::move(request->iterator), std::move(iterator),
[](auto* self, fidl::UnbindInfo info, fidl::ServerEnd<fdd::NodeInfoIterator> server_end) {
if (info.is_user_initiated()) {
return;
}
if (info.is_peer_closed()) {
// For this development protocol, the client is free to disconnect
// at any time.
return;
}
fdf_log::error("Error serving '{}': {}", fidl::DiscoverableProtocolName<fdd::Manager>,
info.FormatDescription());
});
}
void DriverDevelopmentService::GetCompositeInfo(GetCompositeInfoRequestView request,
GetCompositeInfoCompleter::Sync& completer) {
auto arena = std::make_unique<fidl::Arena<512>>();
std::vector<fdd::wire::CompositeNodeInfo> list = driver_runner_.GetCompositeListInfo(*arena);
auto iterator = std::make_unique<driver_development::CompositeInfoIterator>(std::move(arena),
std::move(list));
fidl::BindServer(this->dispatcher_, std::move(request->iterator), std::move(iterator),
[](auto* server, fidl::UnbindInfo info, auto channel) {
if (!info.is_peer_closed()) {
fdf_log::warn("Closed CompositeInfoIterator: {}", info.lossy_description());
}
});
}
void DriverDevelopmentService::GetDriverInfo(GetDriverInfoRequestView request,
GetDriverInfoCompleter::Sync& completer) {
auto driver_index_client = component::Connect<fuchsia_driver_index::DevelopmentManager>();
if (driver_index_client.is_error()) {
fdf_log::error("Failed to connect to service '{}': {}",
fidl::DiscoverableProtocolName<fuchsia_driver_index::DevelopmentManager>,
driver_index_client.status_string());
request->iterator.Close(driver_index_client.status_value());
return;
}
fidl::WireSyncClient driver_index{std::move(*driver_index_client)};
auto info_result =
driver_index->GetDriverInfo(std::move(request->driver_filter), std::move(request->iterator));
if (!info_result.ok()) {
fdf_log::error("Failed to call DriverIndex::GetDriverInfo: {}", info_result.error());
}
}
void DriverDevelopmentService::GetCompositeNodeSpecs(
GetCompositeNodeSpecsRequestView request, GetCompositeNodeSpecsCompleter::Sync& completer) {
auto driver_index_client = component::Connect<fuchsia_driver_index::DevelopmentManager>();
if (driver_index_client.is_error()) {
fdf_log::error("Failed to connect to service '{}': {}",
fidl::DiscoverableProtocolName<fuchsia_driver_index::DevelopmentManager>,
driver_index_client.status_string());
request->iterator.Close(driver_index_client.status_value());
return;
}
fidl::WireSyncClient driver_index{std::move(*driver_index_client)};
auto info_result =
driver_index->GetCompositeNodeSpecs(request->name_filter, std::move(request->iterator));
if (!info_result.ok()) {
fdf_log::error("Failed to call DriverIndex::GetCompositeNodeSpecs: {}", info_result.error());
}
}
void DriverDevelopmentService::GetDriverHostInfo(GetDriverHostInfoRequestView request,
GetDriverHostInfoCompleter::Sync& completer) {}
void DriverDevelopmentService::DisableDriver(DisableDriverRequestView request,
DisableDriverCompleter::Sync& completer) {
auto driver_index_client = component::Connect<fuchsia_driver_index::DevelopmentManager>();
if (driver_index_client.is_error()) {
fdf_log::error("Failed to connect to service '{}': {}",
fidl::DiscoverableProtocolName<fuchsia_driver_index::DevelopmentManager>,
driver_index_client.status_string());
completer.Close(driver_index_client.status_value());
return;
}
fidl::WireSyncClient driver_index{std::move(*driver_index_client)};
auto disable_result = driver_index->DisableDriver(request->driver_url, request->package_hash);
if (!disable_result.ok()) {
fdf_log::error("Failed to call DriverIndex::DisableDriver: {}", disable_result.error());
completer.Close(disable_result.error().status());
return;
}
completer.Reply(disable_result.value());
}
void DriverDevelopmentService::EnableDriver(EnableDriverRequestView request,
EnableDriverCompleter::Sync& completer) {
auto driver_index_client = component::Connect<fuchsia_driver_index::DevelopmentManager>();
if (driver_index_client.is_error()) {
fdf_log::error("Failed to connect to service '{}': {}",
fidl::DiscoverableProtocolName<fuchsia_driver_index::DevelopmentManager>,
driver_index_client.status_string());
completer.Close(driver_index_client.status_value());
return;
}
fidl::WireSyncClient driver_index{std::move(*driver_index_client)};
auto enable_result = driver_index->EnableDriver(request->driver_url, request->package_hash);
if (!enable_result.ok()) {
fdf_log::error("Failed to call DriverIndex::EnableDriver: {}", enable_result.error());
completer.Close(enable_result.error().status());
return;
}
completer.Reply(enable_result.value());
}
void DriverDevelopmentService::RestartDriverHosts(RestartDriverHostsRequestView request,
RestartDriverHostsCompleter::Sync& completer) {
auto result = driver_runner_.RestartNodesColocatedWithDriverUrl(request->driver_url.get(),
request->rematch_flags);
if (result.is_ok()) {
completer.ReplySuccess(result.value());
} else {
completer.ReplyError(result.error_value());
}
}
void DriverDevelopmentService::BindAllUnboundNodes(BindAllUnboundNodesCompleter::Sync& completer) {
auto callback =
[completer = completer.ToAsync()](
fidl::VectorView<fuchsia_driver_development::wire::NodeBindingInfo> result) mutable {
completer.ReplySuccess(result);
};
driver_runner_.TryBindAllAvailable(std::move(callback));
}
void DriverDevelopmentService::BindAllUnboundNodes2(
BindAllUnboundNodes2Completer::Sync& completer) {
auto callback =
[completer = completer.ToAsync()](
fidl::VectorView<fuchsia_driver_development::wire::NodeBindingInfo> result) mutable {
completer.ReplySuccess(result);
};
driver_runner_.TryBindAllAvailable(std::move(callback));
}
void DriverDevelopmentService::AddTestNode(AddTestNodeRequestView request,
AddTestNodeCompleter::Sync& completer) {
fuchsia_driver_framework::NodeAddArgs args;
args.name(fidl::ToNatural(request->args.name()));
args.properties(fidl::ToNatural(request->args.properties()));
driver_runner_.root_node()->AddChild(
std::move(args), /* controller */ {}, /* node */ {},
[this, completer = completer.ToAsync()](fit::result<fuchsia_driver_framework::wire::NodeError,
std::shared_ptr<driver_manager::Node>>
result) mutable {
if (result.is_error()) {
completer.Reply(result.take_error());
} else {
auto node = result.value();
test_nodes_[node->name()] = node;
completer.ReplySuccess();
}
});
}
void DriverDevelopmentService::RemoveTestNode(RemoveTestNodeRequestView request,
RemoveTestNodeCompleter::Sync& completer) {
auto name = std::string(request->name.get());
if (test_nodes_.count(name) == 0) {
completer.ReplyError(ZX_ERR_NOT_FOUND);
return;
}
auto node = test_nodes_[name].lock();
if (!node) {
completer.ReplySuccess();
test_nodes_.erase(name);
return;
}
node->Remove(driver_manager::RemovalSet::kAll, nullptr);
test_nodes_.erase(name);
completer.ReplySuccess();
}
void DriverDevelopmentService::WaitForBootup(WaitForBootupCompleter::Sync& completer) {
driver_runner_.WaitForBootup([completer = completer.ToAsync()]() mutable { completer.Reply(); });
}
void DriverDevelopmentService::RestartWithDictionary(
RestartWithDictionaryRequestView request, RestartWithDictionaryCompleter::Sync& completer) {
zx::eventpair endpoint0, endpoint1;
zx_status_t status = zx::eventpair::create(0, &endpoint0, &endpoint1);
if (status != ZX_OK) {
completer.ReplyError(status);
return;
}
driver_runner_.RestartWithDictionary(std::move(request->moniker), std::move(request->dictionary),
std::move(endpoint1));
completer.ReplySuccess(std::move(endpoint0));
}
void DriverDevelopmentService::RebindCompositesWithDriver(
RebindCompositesWithDriverRequestView request,
RebindCompositesWithDriverCompleter::Sync& completer) {
auto driver_index_client = component::Connect<fuchsia_driver_index::DevelopmentManager>();
if (driver_index_client.is_error()) {
fdf_log::error("Failed to connect to service '{}': {}",
fidl::DiscoverableProtocolName<fuchsia_driver_index::DevelopmentManager>,
driver_index_client.status_string());
completer.Close(driver_index_client.status_value());
return;
}
fidl::WireSyncClient driver_index{std::move(*driver_index_client)};
auto index_rebind_result = driver_index->RebindCompositesWithDriver(request->driver_url);
if (!index_rebind_result.ok()) {
fdf_log::error("Failed to call DriverIndex::RebindCompositesWithDriver: {}",
index_rebind_result.error());
completer.ReplyError(index_rebind_result.error().status());
return;
}
if (index_rebind_result.value().is_error()) {
fdf_log::error("DriverIndex::RebindCompositesWithDriver failed: {}",
zx_status_get_string(index_rebind_result.value().error_value()));
completer.ReplyError(index_rebind_result.value().error_value());
return;
}
driver_runner_.RebindCompositesWithDriver(
std::string(request->driver_url.get()),
[completer = completer.ToAsync()](size_t count) mutable {
completer.ReplySuccess(static_cast<uint32_t>(count));
});
}
void DriverDevelopmentService::handle_unknown_method(
fidl::UnknownMethodMetadata<fuchsia_driver_development::Manager> metadata,
fidl::UnknownMethodCompleter::Sync& completer) {
std::string method_type;
switch (metadata.unknown_method_type) {
case fidl::UnknownMethodType::kOneWay:
method_type = "one-way";
break;
case fidl::UnknownMethodType::kTwoWay:
method_type = "two-way";
break;
};
fdf_log::warn("DriverDevelopmentService received unknown {} method. Ordinal: {}", method_type,
metadata.method_ordinal);
}
} // namespace driver_manager