Skip to main content

driver_manager_node/
composite.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::node::{Node, NodePropertyEntry};
6use crate::node_manager::NodeManager;
7use crate::shutdown::NodeBridge;
8use crate::types::{NodeDictionary, NodeState, NodeTypeVariant};
9use driver_manager_devfs::DevfsDevice;
10use driver_manager_shutdown::{NodeShutdownCoordinator, ShutdownIntent};
11use driver_manager_types::{Collection, NodeOffer, OfferTransport};
12use futures::channel::oneshot;
13use std::cell::RefCell;
14use std::rc::{Rc, Weak};
15use {
16    fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_driver_framework as fdf,
17    fuchsia_async as fasync,
18};
19
20impl Node {
21    pub fn new_composite(
22        name: &str,
23        parents: Vec<Weak<Node>>,
24        parents_names: Vec<String>,
25        primary_index: u32,
26        node_manager: Box<dyn NodeManager>,
27    ) -> Rc<Self> {
28        let initial_host = parents[primary_index as usize].upgrade().and_then(|p| p.driver_host());
29
30        Rc::new_cyclic(|weak_self| {
31            let bridge = Box::new(NodeBridge::new(weak_self.clone()));
32            let enable_test_shutdown_delays = node_manager.is_test_shutdown_delay_enabled();
33            let shutdown_test_rng = node_manager.get_shutdown_test_rng();
34            Self {
35                name: name.to_string(),
36                node_manager,
37                core: RefCell::new(crate::node::NodeCore {
38                    collection: Collection::None,
39                    driver_package_type: fdf::DriverPackageType::Base,
40                    node_type: NodeTypeVariant::Composite {
41                        parents,
42                        parents_names,
43                        primary_index,
44                    },
45                    children: Vec::new(),
46                    properties: Vec::new(),
47                    symbols: Vec::new(),
48                    offers: Vec::new(),
49                    bus_info: None,
50                    dictionary: NodeDictionary::None,
51                }),
52                state: RefCell::new(NodeState::Unbound),
53                devfs: RefCell::new(crate::node::NodeDevfs {
54                    device: DevfsDevice::new(),
55                    protocol_connector: None,
56                    controller_allowlist_passthrough: None,
57                }),
58                shutdown: RefCell::new(crate::node::NodeShutdown {
59                    remove_complete_callback: None,
60                    unbinding_children_completers: Vec::new(),
61                    should_destroy_driver_component: false,
62                }),
63                binding: RefCell::new(crate::node::NodeBinding {
64                    node_controller: None,
65                    pending_bind_completer: None,
66                    bind_error: None,
67                    wait_for_driver_completer: None,
68                    restart_driver_url_suffix: None,
69                    composite_rebind_completer: None,
70                }),
71                component: RefCell::new(None),
72                driver_host: RefCell::new(crate::node::NodeDriverHost {
73                    host: initial_host,
74                    name_for_colocation: String::new(),
75                    restart_on_crash: false,
76                }),
77                node_shutdown_coordinator: RefCell::new(NodeShutdownCoordinator::new(
78                    bridge,
79                    enable_test_shutdown_delays,
80                    shutdown_test_rng,
81                )),
82                can_multibind_composites: true,
83                weak_self: weak_self.clone(),
84                scope: fasync::Scope::new_with_name(format!("node:{name}")),
85            }
86        })
87    }
88
89    pub fn create_composite_offer(
90        offer: &NodeOffer,
91        parents_name: &str,
92        primary_parent: bool,
93    ) -> NodeOffer {
94        let is_default_offer = |name: &str| name == "default";
95
96        let mut new_instance_count = offer.renamed_instances.len();
97        if primary_parent {
98            new_instance_count += offer
99                .renamed_instances
100                .iter()
101                .filter(|instance| is_default_offer(&instance.target_name))
102                .count();
103        }
104
105        let mut new_mappings = Vec::with_capacity(new_instance_count);
106        for instance in &offer.renamed_instances {
107            let target_name = &instance.target_name;
108            if !is_default_offer(target_name) {
109                new_mappings.push(instance.clone());
110                continue;
111            }
112
113            if primary_parent {
114                new_mappings.push(instance.clone());
115            }
116
117            new_mappings.push(fdecl::NameMapping {
118                source_name: instance.source_name.clone(),
119                target_name: parents_name.to_string(),
120            });
121        }
122
123        let mut new_filter_count = offer.source_instance_filter.len();
124        if primary_parent {
125            new_filter_count += offer
126                .source_instance_filter
127                .iter()
128                .filter(|filter| is_default_offer(filter))
129                .count();
130        }
131
132        let mut new_filters = Vec::with_capacity(new_filter_count);
133        for filter in &offer.source_instance_filter {
134            if !is_default_offer(filter) {
135                new_filters.push(filter.clone());
136                continue;
137            }
138
139            if primary_parent {
140                new_filters.push("default".to_string());
141            }
142
143            new_filters.push(parents_name.to_string());
144        }
145
146        NodeOffer {
147            source_name: offer.source_name.clone(),
148            source_collection: offer.source_collection,
149            transport: offer.transport.clone(),
150            service_name: offer.service_name.clone(),
151            source_instance_filter: new_filters,
152            renamed_instances: new_mappings,
153            dir_connector: offer.dir_connector.clone(),
154        }
155    }
156
157    pub fn create_composite_node(
158        node_name: &str,
159        parents: Vec<Weak<Node>>,
160        parents_names: Vec<String>,
161        parent_properties: &[NodePropertyEntry],
162        node_manager: Box<dyn NodeManager>,
163        driver_host_name_for_colocation: String,
164        primary_index: u32,
165    ) -> Result<Rc<Self>, zx::Status> {
166        if parents.is_empty() {
167            return Err(zx::Status::INVALID_ARGS);
168        }
169        if parents.len() != parent_properties.len() {
170            return Err(zx::Status::INVALID_ARGS);
171        }
172        if primary_index as usize >= parents.len() {
173            return Err(zx::Status::OUT_OF_RANGE);
174        }
175
176        let mut has_dictionary_offer = false;
177
178        let mut offers = vec![];
179        for (i, parent) in parents.iter().enumerate() {
180            let parent = parent.upgrade().expect("parent should be alive");
181            for offer in parent.offers().iter() {
182                let new_offer = Self::create_composite_offer(
183                    offer,
184                    &parents_names[i],
185                    i == primary_index as usize,
186                );
187
188                if matches!(new_offer.transport, OfferTransport::Dictionary) {
189                    has_dictionary_offer = true;
190                }
191
192                offers.push(new_offer);
193            }
194        }
195
196        let composite =
197            Self::new_composite(node_name, parents, parents_names, primary_index, node_manager);
198
199        composite.set_driver_host_name_for_colocation(&driver_host_name_for_colocation);
200
201        Self::set_composite_parent_properties(&composite, parent_properties);
202
203        let primary_parent = composite.get_primary_parent().expect("primary parent should exist");
204        let symbols = primary_parent.symbols();
205        composite.set_symbols(symbols);
206
207        // Copy the subtree dictionary of the primary parent node down to the composite.
208        if let NodeDictionary::Subtree(d) = primary_parent.dictionary() {
209            if has_dictionary_offer {
210                panic!("Cannot use dictionary offers on node");
211            }
212
213            composite.set_subtree_dictionary(d);
214        }
215
216        composite.set_offers(offers);
217
218        composite.add_to_parents();
219
220        let primary_parent_device = primary_parent.device();
221        let topological = primary_parent_device.topological.as_ref().unwrap_or_else(|| {
222            panic!(
223                "Missing topological devfs node for primary parent: {}",
224                composite.make_topological_path(false)
225            )
226        });
227
228        // TODO(https://fxbug.dev/331779666): disable controller access for composite nodes
229        let devfs_device = topological.add_child(
230            &composite.name,
231            None,
232            composite.create_devfs_passthrough(None, None, true, "".to_string()),
233        )?;
234        composite.set_device(devfs_device);
235
236        Ok(composite)
237    }
238
239    pub fn set_composite_parent_properties(&self, parent_properties: &[NodePropertyEntry]) {
240        self.clear_properties();
241        self.set_properties(parent_properties.to_vec());
242        let primary_index = if let NodeTypeVariant::Composite { primary_index, .. } = &*self.node_type() {
243            Some(*primary_index)
244        } else {
245            None
246        };
247        if let Some(primary_index) = primary_index {
248            let default_properties = &parent_properties[primary_index as usize].properties;
249            self.push_property(NodePropertyEntry {
250                name: "default".to_string(),
251                properties: default_properties.to_vec(),
252            });
253        }
254    }
255
256    pub fn remove_composite_node_for_rebind(
257        self: &Rc<Self>,
258        completer: oneshot::Sender<Result<(), zx::Status>>,
259    ) {
260        if let Err(completer) = self.set_composite_rebind_completer(completer) {
261            let _ = completer.send(Err(zx::Status::ALREADY_EXISTS));
262            return;
263        }
264
265        self.get_shutdown_coordinator().set_shutdown_intent(ShutdownIntent::RebindComposite);
266        self.remove(driver_manager_shutdown::RemovalSet::All, None);
267    }
268}