| // 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. |
| |
| #ifndef SRC_DEVICES_BIN_DRIVER_MANAGER_NODE_H_ |
| #define SRC_DEVICES_BIN_DRIVER_MANAGER_NODE_H_ |
| |
| #include <fidl/fuchsia.component.runner/cpp/wire.h> |
| #include <fidl/fuchsia.driver.development/cpp/fidl.h> |
| #include <fidl/fuchsia.driver.framework/cpp/fidl.h> |
| #include <fidl/fuchsia.driver.host/cpp/wire.h> |
| #include <fidl/fuchsia.driver.index/cpp/wire.h> |
| #include <fidl/fuchsia.power.broker/cpp/fidl.h> |
| #include <lib/zx/event.h> |
| #include <zircon/assert.h> |
| #include <zircon/types.h> |
| |
| #include <list> |
| #include <memory> |
| #include <utility> |
| #include <variant> |
| |
| #include "src/devices/bin/driver_manager/bind/bind_result_tracker.h" |
| #include "src/devices/bin/driver_manager/component_owner.h" |
| #include "src/devices/bin/driver_manager/controller_allowlist_passthrough.h" |
| #include "src/devices/bin/driver_manager/devfs/devfs.h" |
| #include "src/devices/bin/driver_manager/dictionary_util.h" |
| #include "src/devices/bin/driver_manager/driver_host.h" |
| #include "src/devices/bin/driver_manager/memory_attribution.h" |
| #include "src/devices/bin/driver_manager/node_types.h" |
| #include "src/devices/bin/driver_manager/shutdown/node_removal_tracker.h" |
| #include "src/devices/bin/driver_manager/shutdown/node_shutdown_coordinator.h" |
| |
| namespace driver_manager { |
| |
| enum class OfferTransport : std::uint8_t { |
| DriverTransport, |
| ZirconTransport, |
| Dictionary, |
| }; |
| |
| struct NodeOffer { |
| std::string source_name; |
| Collection source_collection; |
| OfferTransport transport; |
| std::string service_name; |
| std::vector<std::string> source_instance_filter; |
| std::vector<fuchsia_component_decl::NameMapping> renamed_instances; |
| }; |
| |
| fuchsia_driver_framework::Offer ToFidl(const NodeOffer& offer); |
| |
| // This function creates a composite offer based on a service offer. |
| NodeOffer CreateCompositeOffer(const NodeOffer& offer, std::string_view parents_name, |
| bool primary_parent); |
| |
| class Node; |
| struct NodeInfo; |
| class NodeRemovalTracker; |
| class BootupTracker; |
| struct PowerElementStartArgs; |
| |
| class DriverHostConnection : public fidl::WireAsyncEventHandler<fuchsia_driver_host::Driver> { |
| public: |
| explicit DriverHostConnection(Node* node) : node_(node) {} |
| |
| void on_fidl_error(fidl::UnbindInfo info) override; |
| |
| private: |
| Node* node_; |
| }; |
| |
| class ComponentControllerConnection |
| : public fidl::WireAsyncEventHandler<fuchsia_component::Controller> { |
| public: |
| explicit ComponentControllerConnection(Node* node) : node_(node) {} |
| |
| void on_fidl_error(fidl::UnbindInfo info) override; |
| |
| void handle_unknown_event( |
| fidl::UnknownEventMetadata<fuchsia_component::Controller> metadata) override {} |
| |
| private: |
| Node* node_; |
| }; |
| |
| using AddNodeResultCallback = |
| fit::callback<void(fit::result<fuchsia_driver_framework::NodeError, std::shared_ptr<Node>>)>; |
| |
| using OnBindWaitCompleter = |
| fit::callback<void(zx::result<fuchsia_driver_framework::wire::DriverResult>)>; |
| |
| struct PowerElementHandles { |
| fidl::Client<fuchsia_power_broker::ElementControl> element_control; |
| fidl::ServerEnd<fuchsia_power_broker::ElementRunner> element_runner; |
| fidl::Client<fuchsia_power_broker::Lessor> lessor; |
| }; |
| |
| class NodeManager { |
| public: |
| virtual ~NodeManager() = default; |
| |
| // Attempt to bind `node`. |
| // A nullptr for result_tracker is acceptable if the caller doesn't intend to |
| // track the results. |
| virtual void Bind(Node& node, std::shared_ptr<BindResultTracker> result_tracker) = 0; |
| |
| virtual void BindToUrl(Node& node, std::string_view driver_url_suffix, |
| std::shared_ptr<BindResultTracker> result_tracker) { |
| ZX_PANIC("Unimplemented BindToUrl"); |
| } |
| |
| virtual zx::result<> StartDriver(Node& node, std::string_view url, |
| fuchsia_driver_framework::DriverPackageType package_type) { |
| return zx::error(ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| virtual DriverHost* GetDriverHost(std::string_view driver_name_name_for_colocation) = 0; |
| |
| virtual zx::result<DriverHost*> CreateDriverHost( |
| bool use_next_vdso, std::string_view driver_name_name_for_colocation) = 0; |
| |
| // Creates the driver host component, loads the driver host using dynamic linking, |
| // and calls |cb| on completion or error. |
| virtual void CreateDriverHostDynamicLinker( |
| std::string_view driver_name_name_for_colocation, |
| fit::callback<void(zx::result<DriverHost*>)> completion_cb) { |
| completion_cb(zx::error(ZX_ERR_NOT_SUPPORTED)); |
| } |
| |
| // DriverHost lifetimes are managed through a linked list, and they will delete themselves |
| // when the FIDL connection is closed. Currently in the Node class we store a raw pointer to the |
| // DriverHost object, and do not have a way to remove it from the class when the underlying |
| // DriverHost object is deallocated. This function will return true if the underlying DriverHost |
| // object is still alive and in the linked list. Otherwise returns false. |
| virtual bool IsDriverHostValid(DriverHost* driver_host) const { return true; } |
| |
| virtual void RebindComposite(std::string spec, std::optional<std::string> driver_url, |
| fit::callback<void(zx::result<>)> callback) {} |
| |
| virtual bool IsTestShutdownDelayEnabled() const { return false; } |
| virtual std::weak_ptr<std::mt19937> GetShutdownTestRng() const { |
| return std::weak_ptr<std::mt19937>(); |
| } |
| |
| virtual void WaitForBootup(fit::callback<void()> callback) { callback(); } |
| |
| virtual void ImportDictionary(fuchsia_component_sandbox::DictionaryRef dictionary, |
| fit::callback<void(zx::result<uint64_t>)> callback) { |
| callback(zx::error(ZX_ERR_NOT_SUPPORTED)); |
| } |
| |
| // Create a power element where |element_token| is the access token for the newly created |
| // element. |deps| are the tokens this element should depend on. |
| // |cb| is called with: |
| // - `zx::ok(true)` if the power element was successfully created |
| // - `zx::ok(false)` if the power element could not be created because this is not a suspend- |
| // enabled platform. |
| // - `zx::error` if an error happens creating the element on a suspend-enabled platform. |
| virtual void CreatePowerElement(std::string_view name, |
| fuchsia_power_broker::DependencyToken element_token, |
| std::span<fuchsia_power_broker::DependencyToken>& deps, |
| fidl::ServerEnd<fuchsia_power_broker::ElementControl> control, |
| fidl::ClientEnd<fuchsia_power_broker::ElementRunner> runner, |
| fidl::ServerEnd<fuchsia_power_broker::Lessor> lessor, |
| fit::callback<void(zx::result<bool>)> cb) { |
| cb(zx::error(ZX_ERR_NOT_SUPPORTED)); |
| } |
| |
| // Store the `fuchsia.power.broker/LeaseControl` channel associated with the power element for |
| // this element. |
| virtual void AddLeaseControlChannel(fidl::ClientEnd<fuchsia_power_broker::LeaseControl> lease) { |
| ZX_PANIC("Unimplemented AddLeaseControlChannel"); |
| } |
| |
| virtual DictionaryUtil& dictionary_util() { ZX_PANIC("Unimplemented dictionary_util"); } |
| virtual bool SuspendEnabled() { ZX_PANIC("Unimplemented SuspendEnabled"); } |
| virtual MemoryAttributor& memory_attributor() { ZX_PANIC("Unimplemented memory_attributor"); } |
| }; |
| |
| class Node : public fidl::WireServer<fuchsia_driver_framework::NodeController>, |
| public fidl::WireServer<fuchsia_driver_framework::Node>, |
| public fidl::WireServer<fuchsia_component_runner::ComponentController>, |
| public fidl::WireServer<fuchsia_device::Controller>, |
| public std::enable_shared_from_this<Node>, |
| public NodeShutdownBridge, |
| public ComponentOwner { |
| friend DriverHostConnection; |
| friend ComponentControllerConnection; |
| |
| public: |
| Node(std::string_view name, std::weak_ptr<Node> parent, NodeManager* node_manager, |
| async_dispatcher_t* dispatcher); |
| Node(std::string_view name, std::vector<std::weak_ptr<Node>> parents, |
| std::vector<std::string> parents_names, NodeManager* node_manager, |
| async_dispatcher_t* dispatcher, uint32_t primary_index); |
| |
| ~Node() override; |
| |
| static zx::result<std::shared_ptr<Node>> CreateCompositeNode( |
| std::string_view node_name, std::vector<std::weak_ptr<Node>> parents, |
| std::vector<std::string> parents_names, |
| const std::vector<fuchsia_driver_framework::NodePropertyEntry2>& parent_properties, |
| NodeManager* driver_binder, async_dispatcher_t* dispatcher, |
| std::string_view driver_host_name_for_colocation, uint32_t primary_index = 0); |
| |
| // This is called when |node_ref_| is unbound from the dispatcher. |
| void OnNodeServerUnbound(fidl::UnbindInfo info); |
| |
| // NodeShutdownBridge |
| // Exposed for testing. |
| bool HasDriverComponent() const override { |
| auto* driver_component = std::get_if<DriverComponent>(&state_); |
| return driver_component && driver_component->state != DriverState::kStopped; |
| } |
| |
| void OnBind(); |
| void OnMatchError(zx_status_t status); |
| void OnStartError(zx_status_t status); |
| |
| bool HasDriverComponentController() const override { return component_controller_.is_valid(); } |
| |
| bool is_bound() const { return std::holds_alternative<DriverComponent>(state_); } |
| |
| // Begin the removal process for a Node. This function ensures that a Node is |
| // only removed after all of its children are removed. It also ensures that |
| // a Node is only removed after the driver that is bound to it has been stopped. |
| // This is safe to call multiple times. |
| // There are multiple reasons a Node's removal will be started: |
| // - The system is being stopped. |
| // - The Node had an unexpected error or disconnect |
| // During a system stop, Remove is expected to be called twice: |
| // once with |removal_set| == kPackage, and once with |removal_set| == kAll. |
| // Errors and disconnects that are unrecoverable should call Remove(kAll, nullptr). |
| void Remove(RemovalSet removal_set, NodeRemovalTracker* removal_tracker); |
| |
| // `callback` is invoked once the node has finished being added or an error |
| // has occurred. |
| void AddChild(fuchsia_driver_framework::NodeAddArgs args, |
| fidl::ServerEnd<fuchsia_driver_framework::NodeController> controller, |
| fidl::ServerEnd<fuchsia_driver_framework::Node> node, |
| AddNodeResultCallback callback); |
| |
| // Add this Node to its parents. This should be called when the node is created. Exposed for |
| // testing. |
| void AddToParents(); |
| |
| // Begins the process of restarting the node. Restarting a node includes stopping and removing |
| // all children nodes, stopping the driver that is bound to the node, and asking the NodeManager |
| // to bind the node again. The restart operation is very similar to the Remove operation, the |
| // difference being once the children are removed, and the driver stopped, we don't remove the |
| // node from the topology but instead bind the node again. |
| void RestartNode(); |
| |
| // Begins the process of quarantining the node. This is basically performing a Remove, |
| // but instead of removing the node from the topology, we keep it in a stopped state so that it |
| // can be orphaned if its driver is ever disabled. That way new drivers can be bound to the node. |
| void QuarantineNode(); |
| |
| void RemoveCompositeNodeForRebind(fit::callback<void(zx::result<>)> completer); |
| |
| // Restarting a node WithRematch, means that instead of re-using the currently bound driver, |
| // another MatchDriver call will be made into the driver index to find a new driver to bind. |
| void RestartNodeWithRematch(std::optional<std::string> restart_driver_url_suffix, |
| fit::callback<void(zx::result<>)> completer); |
| void RestartNodeWithRematch(); |
| |
| void StartDriver( |
| fuchsia_component_runner::wire::ComponentStartInfo start_info, |
| fidl::ServerEnd<fuchsia_component_runner::ComponentController> component_controller, |
| fit::callback<void(zx::result<>)> cb); |
| |
| void LeaseDriverPowerElement(fit::callback<void(zx::result<>)> cb); |
| |
| // ComponentOwner |
| void SetController(fidl::ClientEnd<fuchsia_component::Controller> component_controller) override; |
| void OnComponentStarted(const std::weak_ptr<BootupTracker>& bootup_tracker, |
| const std::string& moniker, |
| zx::result<StartedComponent> component) override; |
| void RequestStartComponent(fuchsia_process::wire::HandleInfo startup_handle, |
| const std::string& moniker, |
| const std::weak_ptr<BootupTracker>& bootup_tracker) override; |
| |
| bool IsComposite() const { return std::holds_alternative<Composite>(type_); } |
| |
| // Exposed for testing. |
| // Set properties to non-composite node properties containing a clone of `properties`. |
| void SetNonCompositeProperties( |
| std::span<const fuchsia_driver_framework::NodeProperty2> properties); |
| |
| // Evaluates the given rematch_flags against the node. Returns true if rematch should take place, |
| // false otherwise. Rematching is done based on the node type and url both matching: |
| // For node type, if the node is a composite, the rematch flags must contain the flag |
| // for the composite variant that the node is. No validation for non-composites. |
| // For the url, rematch takes place if either: |
| // - the url matches the requested_url and the 'requested' flag is available. |
| // - the url does not match and the 'non_requested' flag is available. |
| bool EvaluateRematchFlags(fuchsia_driver_development::RestartRematchFlags rematch_flags, |
| std::string_view requested_url) const; |
| |
| // Creates the node's topological path by combining each primary parent's name together, |
| // separated by '/'. |
| // E.g: dev/sys/topo/path |
| std::string MakeTopologicalPath(bool deduplicate = false) const; |
| |
| // Make the node's component moniker by making the topological path and then replacing |
| // characters not allowed by the component framework. |
| // E.g: dev.sys.topo.path |
| std::string MakeComponentMoniker() const; |
| |
| // Exposed for testing. |
| Node* GetPrimaryParent() const { |
| if (auto* composite = std::get_if<Composite>(&type_); composite) { |
| if (composite->primary_index_ < composite->parents_.size()) { |
| return composite->parents_[composite->primary_index_].lock().get(); |
| } |
| return nullptr; |
| } |
| auto parent = std::get<Normal>(type_).parent_.lock(); |
| return parent ? parent.get() : nullptr; |
| } |
| |
| // This should be used on the root node. Install the root node at the top of the devfs filesystem. |
| void SetupDevfsForRootNode(std::shared_ptr<Devfs>& devfs) { |
| devfs = std::make_shared<Devfs>(devfs_device_.topological_node(), dispatcher_); |
| } |
| |
| // This is exposed for testing. Setup this node's devfs nodes. |
| void AddToDevfsForTesting(Devnode& parent) { |
| parent.add_child(name_, std::nullopt, Devnode::Target(), devfs_device_); |
| } |
| |
| void SetShouldDestroy(); |
| |
| // Invoked when a bind sequence has been completed. It allows us to reply to outstanding bind |
| // requests that may have originated from the node. |
| void CompleteBind(zx::result<> result); |
| |
| NodeShutdownCoordinator& GetNodeShutdownCoordinator(); |
| |
| NodeState GetNodeState() { return GetNodeShutdownCoordinator().node_state(); } |
| |
| const std::string& name() const { return name_; } |
| |
| NodeType type() const { |
| return std::holds_alternative<Normal>(type_) ? NodeType::kNormal : NodeType::kComposite; |
| } |
| |
| const DriverHost* driver_host() const { |
| if (node_manager_.has_value() && node_manager_.value()->IsDriverHostValid(*driver_host_)) { |
| return *driver_host_; |
| } |
| |
| return nullptr; |
| } |
| |
| DriverHost* driver_host() { |
| if (node_manager_.has_value() && node_manager_.value()->IsDriverHostValid(*driver_host_)) { |
| return *driver_host_; |
| } |
| |
| return nullptr; |
| } |
| |
| const std::string& driver_url() const; |
| |
| bool quarantined() const { return std::holds_alternative<Quarantined>(state_); } |
| |
| std::span<const std::weak_ptr<Node>> parents() const { |
| if (IsComposite()) { |
| return std::get<Composite>(type_).parents_; |
| } |
| if (auto& parent = std::get<Normal>(type_).parent_; parent.lock() != nullptr) { |
| return std::span(&parent, 1); |
| } |
| return {}; |
| } |
| |
| const std::list<std::shared_ptr<Node>>& children() const { return children_; } |
| |
| const std::vector<NodeOffer>& offers() const { return offers_; } |
| |
| const std::vector<fuchsia_driver_framework::NodeSymbol>& symbols() const { return symbols_; } |
| |
| // Returns the node properties of the node and its parents if the node is a composite node. |
| // See `properties_` property for more info. |
| size_t properties_size() const { return properties_.size(); } |
| |
| // Returns the node properties of the node or the node's parent if the node is a composite node. |
| // Returns std::nullopt if the node is a non-composite and `parent_name` is not "default". |
| // Returns std::nullopt if the parent node cannot be found. |
| // See `properties_` property for more info. |
| std::optional<std::vector<fuchsia_driver_framework::NodeProperty2>> GetNodeProperties( |
| std::string_view parent_name = "default") const; |
| |
| fuchsia_driver_framework::NodePropertyDictionary2 GetNodePropertyDict() const; |
| |
| void PrepareDictionary(fit::callback<void(zx::result<>)> callback); |
| |
| void SetSubtreeDictionaryRef( |
| std::optional<fuchsia_component_sandbox::CapabilityId> subtree_dictionary_ref) { |
| if (subtree_dictionary_ref.has_value()) { |
| ZX_ASSERT_MSG(!dictionary_ref_.has_value(), "cannot use SubtreeDictionaryRef on this node"); |
| } |
| subtree_dictionary_ref_ = subtree_dictionary_ref; |
| } |
| |
| void MarkAsCompositeParent() { state_ = CompositeParent{}; } |
| |
| void UnmarkAsCompositeParent() { state_ = Unbound{}; } |
| |
| bool HasSubtreeDictionaryRef() const { return subtree_dictionary_ref_.has_value(); } |
| |
| bool SkipInjectedOffers() const override { return HasSubtreeDictionaryRef(); } |
| |
| std::optional<fuchsia_component_sandbox::DictionaryRef> TakeDictionary() override { |
| std::optional<fuchsia_component_sandbox::DictionaryRef> temp; |
| std::swap(temp, offers_dictionary_); |
| return temp; |
| } |
| |
| const Collection& collection() const { return collection_; } |
| |
| const fuchsia_driver_framework::DriverPackageType& driver_package_type() const { |
| return driver_package_type_; |
| } |
| |
| DevfsDevice& devfs_device() { return devfs_device_; } |
| |
| bool can_multibind_composites() const { return can_multibind_composites_; } |
| |
| void set_collection(Collection collection) { collection_ = collection; } |
| |
| void set_driver_package_type(fuchsia_driver_framework::DriverPackageType driver_package_type) { |
| driver_package_type_ = driver_package_type; |
| } |
| |
| void set_symbols(std::vector<fuchsia_driver_framework::NodeSymbol> symbols) { |
| symbols_ = std::move(symbols); |
| } |
| |
| void set_can_multibind_composites(bool can_multibind_composites) { |
| can_multibind_composites_ = can_multibind_composites; |
| } |
| |
| std::optional<zx_koid_t> token_koid() const { |
| auto* driver_component = std::get_if<DriverComponent>(&state_); |
| return driver_component ? std::optional(driver_component->component_instance_koid) |
| : std::nullopt; |
| } |
| std::vector<fuchsia_driver_framework::BusInfo> GetBusTopology() const; |
| |
| ShutdownIntent shutdown_intent() { return GetNodeShutdownCoordinator().shutdown_intent(); } |
| |
| private: |
| struct DriverComponent { |
| DriverComponent( |
| Node& node, std::string url, |
| fidl::ServerEnd<fuchsia_component_runner::ComponentController> component_controller, |
| fidl::ServerEnd<fuchsia_driver_framework::Node> node_server, |
| fidl::ClientEnd<fuchsia_driver_host::Driver> driver, zx::event component_instance); |
| |
| // This represents the Driver Component instance within the Component Framework. |
| // When we send the OnStop event through it, that signals to the Component Framework |
| // that this driver component instance has stopped. |
| fidl::ServerBinding<fuchsia_component_runner::ComponentController> |
| runner_component_controller_ref; |
| fidl::ServerBinding<fuchsia_driver_framework::Node> node_ref_; |
| fidl::WireClient<fuchsia_driver_host::Driver> driver; |
| std::string driver_url; |
| zx::event component_instance; |
| zx_koid_t component_instance_koid = 0; |
| |
| DriverState state = DriverState::kBinding; |
| }; |
| |
| struct EnumValue { |
| std::string value; |
| }; |
| using PropertyValue = std::variant<std::monostate, uint32_t, std::string, bool, EnumValue>; |
| |
| struct Property { |
| std::string key; |
| PropertyValue value; |
| }; |
| |
| struct PropertiesEntry { |
| std::string name; |
| std::vector<Property> properties; |
| }; |
| |
| // fidl::WireServer<fuchsia_device::Controller> |
| void ConnectToDeviceFidl(ConnectToDeviceFidlRequestView request, |
| ConnectToDeviceFidlCompleter::Sync& completer) override; |
| void ConnectToController(ConnectToControllerRequestView request, |
| ConnectToControllerCompleter::Sync& completer) override; |
| void Bind(BindRequestView request, BindCompleter::Sync& completer) override; |
| void Rebind(RebindRequestView request, RebindCompleter::Sync& completer) override; |
| void UnbindChildren(UnbindChildrenCompleter::Sync& completer) override; |
| void ScheduleUnbind(ScheduleUnbindCompleter::Sync& completer) override; |
| void GetTopologicalPath(GetTopologicalPathCompleter::Sync& completer) override; |
| |
| void OnDriverHostFidlError(fidl::UnbindInfo info); |
| void OnComponentControllerFidlError(fidl::UnbindInfo info); |
| |
| // fidl::WireServer<fuchsia_component_runner::ComponentController> |
| void handle_unknown_method( |
| fidl::UnknownMethodMetadata<fuchsia_component_runner::ComponentController> metadata, |
| fidl::UnknownMethodCompleter::Sync& completer) override; |
| |
| void Stop(StopCompleter::Sync& completer) override; |
| void Kill(KillCompleter::Sync& completer) override; |
| |
| // fidl::WireServer<fuchsia_driver_framework::NodeController> |
| void Remove(RemoveCompleter::Sync& completer) override; |
| void RequestBind(RequestBindRequestView request, RequestBindCompleter::Sync& completer) override; |
| void WaitForDriver(WaitForDriverCompleter::Sync& completer) override; |
| void handle_unknown_method( |
| fidl::UnknownMethodMetadata<fuchsia_driver_framework::NodeController> metadata, |
| fidl::UnknownMethodCompleter::Sync& completer) override; |
| |
| // fidl::WireServer<fuchsia_driver_framework::Node> |
| void AddChild(AddChildRequestView request, AddChildCompleter::Sync& completer) override; |
| void handle_unknown_method(fidl::UnknownMethodMetadata<fuchsia_driver_framework::Node> metadata, |
| fidl::UnknownMethodCompleter::Sync& completer) override; |
| |
| // NodeShutdownBridge |
| NodeInfo GetRemovalTrackerInfo() override; |
| void StopDriver() override; |
| void StopDriverComponent() override; |
| bool MaybeDestroyDriverComponent() override; |
| void FinishShutdown(fit::callback<void()> shutdown_callback) override; |
| bool HasChildren() const override { return !children_.empty(); } |
| bool HasDriver() const override { |
| return std::holds_alternative<DriverComponent>(state_) && |
| std::get<DriverComponent>(state_).driver; |
| } |
| |
| bool IsPendingBind() const override { |
| auto* driver_component = std::get_if<DriverComponent>(&state_); |
| if (!driver_component) { |
| return false; |
| } |
| return driver_component->driver && driver_component->state == DriverState::kBinding; |
| } |
| |
| void BindHelper(bool force_rebind, std::optional<std::string> driver_url_suffix, |
| fit::callback<void(zx_status_t)> on_bind_complete); |
| |
| // Shutdown helpers: |
| // Remove a child from this parent |
| void RemoveChild(const std::shared_ptr<Node>& child); |
| |
| // Start the node's driver back up. |
| void FinishRestart(); |
| |
| // Called to wrap things up before going to the quarantine state. |
| void FinishQuarantine(); |
| |
| // Clear out the values associated with the driver on the driver host. |
| void ClearHostDriver(); |
| |
| // Call `callback` once child node with the name `name` has been removed. |
| // Returns an error if a child node with the name `name` exists and is not in |
| // the process of being removed. |
| void WaitForChildToExit( |
| std::string_view name, |
| fit::callback<void(fit::result<fuchsia_driver_framework::NodeError>)> callback); |
| |
| std::shared_ptr<BindResultTracker> CreateBindResultTracker(bool silent = false); |
| |
| void AddChildHelper(fuchsia_driver_framework::NodeAddArgs args, |
| fidl::ServerEnd<fuchsia_driver_framework::NodeController> controller, |
| fidl::ServerEnd<fuchsia_driver_framework::Node> node, |
| AddNodeResultCallback callback); |
| |
| // Creates a passthrough for the associated devfs node that will connect to |
| // the device controller of this node if no connector provided, or the connection |
| // type is not supported. |
| Devnode::Target CreateDevfsPassthrough( |
| std::optional<fidl::ClientEnd<fuchsia_device_fs::Connector>> connector, |
| std::optional<fidl::ClientEnd<fuchsia_device_fs::Connector>> controller_connector, |
| bool allow_controller_connection, const std::string& class_name); |
| |
| zx_status_t ConnectDeviceInterface(zx::channel channel); |
| |
| static std::vector<Property> ToProperty( |
| std::span<const fuchsia_driver_framework::NodeProperty2> properties); |
| static std::vector<fuchsia_driver_framework::NodeProperty2> PropertyToFidl( |
| std::span<const Property> properties); |
| |
| // Set properties to composite node properties containing a clone of the node properties of |
| // `parents_`. |
| void SetCompositeParentProperties( |
| const fuchsia_driver_framework::NodePropertyDictionary2& parent_properties); |
| |
| // Update `properties_dict_` to identify the contents of `properties_`. |
| void StartDriverWithDynamicLinker( |
| DriverHost::DriverLoadArgs load_args, DriverHost::DriverStartArgs start_args, |
| std::string_view url, |
| fidl::ServerEnd<fuchsia_component_runner::ComponentController> component_controller, |
| PowerElementStartArgs power_element_args, fit::callback<void(zx::result<>)> cb); |
| |
| // Creates a driver host, if necessary, and then starts the driver with the dynamic linker. If |
| // |colocate| is true a driver host is not created because we use an existing one. |
| void CreateHostAndStartDriverWithDynamicLinker( |
| DriverHost::DriverLoadArgs load_args, DriverHost::DriverStartArgs start_args, |
| std::string_view url, |
| fidl::ServerEnd<fuchsia_component_runner::ComponentController> component_controller, |
| PowerElementStartArgs power_element_args, bool found_driver_host, |
| fit::callback<void(zx::result<>)> cb); |
| |
| zx::result<zx::event> DuplicateNodeToken(); |
| |
| // Returns a duplicate of the node's power element token, if available. |
| // |
| // If the node has no power token, this will return an invalid zx::event. |
| zx::event DuplicatePowerToken() { |
| if (!power_element_token_.is_valid()) { |
| return zx::event(); |
| } |
| zx::event dupe; |
| zx_status_t dupe_result = power_element_token_.duplicate(ZX_RIGHT_SAME_RIGHTS, &dupe); |
| ZX_ASSERT_MSG(dupe_result == ZX_OK, "Element token duplication failed."); |
| return dupe; |
| } |
| |
| // Return the handle ID of the power token. This should only be used for |
| // examining handle identity, not for any actual operations on the Zircon |
| // object. |
| zx_handle_t GetPowerTokenHandle() { return power_element_token_.get(); } |
| |
| std::string name_; |
| |
| struct Normal { |
| std::weak_ptr<Node> parent_; |
| }; |
| struct Composite { |
| std::vector<std::weak_ptr<Node>> parents_; |
| std::vector<std::string> parents_names_; |
| uint32_t primary_index_; |
| }; |
| |
| std::variant<Normal, Composite> type_; |
| |
| std::list<std::shared_ptr<Node>> children_; |
| fit::nullable<NodeManager*> node_manager_; |
| async_dispatcher_t* const dispatcher_; |
| |
| // TODO(https://fxbug.dev/42073492): Set this flag from NodeAddArgs. |
| bool can_multibind_composites_ = true; |
| |
| bool host_restart_on_crash_ = false; |
| |
| std::vector<NodeOffer> offers_; |
| std::vector<fuchsia_driver_framework::NodeSymbol> symbols_; |
| |
| // Contains the properties of the node or its parents if the node is a composite. |
| // "default" entry refers to the node's properties if the node is a non-composite. |
| // "default" entry refers to the primary parent node's properties if the node is a |
| // composite. |
| std::vector<PropertiesEntry> properties_; |
| |
| std::optional<fuchsia_driver_framework::BusInfo> bus_info_; |
| |
| // A component framework dictionary that should be provided to the driver |
| // that binds to this node. |
| // This ref will become an offers_dictionary_ in PrepareDictionary before the driver starts. |
| // |
| // The difference between |subtree_dictionary_ref_| and |dictionary_ref_| is the subtree one |
| // is always passed down the node tree to children, as it contains non-driver protocol |
| // capabilities for testing (the ones injected in offer_injection). This is only used for system |
| // testing at the moment through |RestartWithDictionary|. |dictionary_ref_| is the node specific |
| // dictionary that contains offers that the node provides that are type |kDictionaryOffer|. |
| // These two cannot be used together as it is. |
| std::optional<fuchsia_component_sandbox::CapabilityId> subtree_dictionary_ref_; |
| std::optional<fuchsia_component_sandbox::CapabilityId> dictionary_ref_; |
| std::optional<fuchsia_component_sandbox::DictionaryRef> offers_dictionary_; |
| std::list<DirReceiverImpl> dir_receivers_; |
| |
| Collection collection_ = Collection::kNone; |
| fuchsia_driver_framework::DriverPackageType driver_package_type_; |
| // If specified by the parent in NodeAddArgs, this will be used to determine which driver host to |
| // load into. |
| std::string driver_host_name_for_colocation_; |
| fit::nullable<DriverHost*> driver_host_; |
| |
| // An outstanding rebind request. |
| fit::callback<void(zx::result<>)> pending_bind_completer_; |
| |
| // Set by RemoveCompositeNodeForRebind(). Invoked when the node finished shutting down. |
| fit::callback<void(zx::result<>)> composite_rebind_completer_; |
| |
| std::optional<std::string> restart_driver_url_suffix_; |
| |
| // Invoked when the node has been fully removed. |
| fit::callback<void()> remove_complete_callback_; |
| bool should_destroy_ = false; |
| fidl::WireClient<fuchsia_component::Controller> component_controller_; |
| |
| struct Unbound {}; |
| struct Starting { |
| std::string driver_url; |
| }; |
| struct OwnedByParent { |
| explicit OwnedByParent(fidl::ServerEnd<fuchsia_driver_framework::Node> node, Node* child); |
| fidl::ServerBinding<fuchsia_driver_framework::Node> node_ref_; |
| }; |
| struct CompositeParent {}; |
| struct Quarantined { |
| std::string driver_url; |
| }; |
| // Valid State transitions: |
| // * Unbound -> Starting, OwnedByParent, CompositeParent |
| // * Starting -> Unbound, DriverComponent, Quarantined |
| // * OwnedByParent -> Unbound |
| // * CompositeParent -> Unbound |
| // * DriverComponent -> Unbound, Quarantined |
| std::variant<Unbound, Starting, OwnedByParent, CompositeParent, DriverComponent, Quarantined> |
| state_; |
| |
| std::optional<fidl::ServerBinding<fuchsia_driver_framework::NodeController>> controller_ref_; |
| OnBindWaitCompleter bind_wait_completer_; |
| std::optional<fuchsia_driver_framework::wire::DriverResult> bind_err_; |
| |
| std::unique_ptr<NodeShutdownCoordinator> node_shutdown_coordinator_; |
| |
| // This represents the node's presence in devfs, both it's topological path and it's class path. |
| DevfsDevice devfs_device_; |
| |
| // Connector to service exported to devfs |
| std::optional<fidl::ClientEnd<fuchsia_device_fs::Connector>> devfs_connector_; |
| // Connector to fuchsia_device/Controller exported to devfs. |
| // This is only used by the compat shim to override the node's own controller |
| // implementation. |
| fidl::ServerBindingGroup<fuchsia_device::Controller> dev_controller_bindings_; |
| std::unique_ptr<ControllerAllowlistPassthrough> controller_allowlist_passthrough_; |
| |
| // Completers that are waiting for the node to unbind all of its children. |
| std::vector<UnbindChildrenCompleter::Async> unbinding_children_completers_; |
| |
| // Handlers for FIDL connections. |
| DriverHostConnection driver_host_handler_; |
| ComponentControllerConnection component_controller_handler_; |
| |
| fuchsia_power_broker::DependencyToken power_element_token_; |
| std::optional<PowerElementHandles> pe_handles_; |
| }; |
| |
| } // namespace driver_manager |
| |
| #endif // SRC_DEVICES_BIN_DRIVER_MANAGER_NODE_H_ |