Skip to main content

driver_manager_node/
add.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::types::{NodeDictionary, NodeState};
7use driver_manager_types::{Collection, NodeOffer, OfferTransport, to_property2};
8use fidl::endpoints::ServerEnd;
9use fidl_fuchsia_component_decl as fdecl;
10use fidl_fuchsia_device_fs as fdevfs;
11use fidl_fuchsia_driver_framework as fdf;
12use futures::channel::oneshot;
13use log::{error, warn};
14use std::cell::RefCell;
15use std::collections::HashSet;
16use std::rc::Rc;
17
18impl Node {
19    pub async fn add_child(
20        self: &Rc<Self>,
21        mut args: fdf::NodeAddArgs,
22        controller: Option<ServerEnd<fdf::NodeControllerMarker>>,
23        node: Option<ServerEnd<fdf::NodeMarker>>,
24    ) -> Result<Rc<Node>, fdf::NodeError> {
25        let name = args.name.ok_or(fdf::NodeError::NameMissing)?;
26
27        if args.properties.is_some() && args.properties2.is_some() {
28            return Err(fdf::NodeError::UnsupportedArgs);
29        }
30
31        self.wait_for_child_to_exit(&name).await?;
32
33        let child = Node::new(&name, self.weak_self.clone(), self.node_manager.clone_box());
34
35        let mut properties = if let Some(props) = args.properties2 {
36            props
37        } else if let Some(props) = args.properties {
38            props.into_iter().map(|prop| to_property2(&prop)).collect()
39        } else {
40            vec![]
41        };
42
43        let has_manual_service_property =
44            properties.iter().any(|prop| prop.key == bind_fuchsia::SERVICE);
45
46        let mut has_dictionary_offer = false;
47
48        if let Some(offers) = args.offers2 {
49            let mut source_node = Some(self.clone());
50            while source_node.is_some()
51                && source_node.as_ref().unwrap().collection() == Collection::None
52            {
53                let current_node = source_node.unwrap();
54                source_node = current_node.get_primary_parent();
55            }
56
57            let (source_name, source_collection) = if let Some(source_node) = source_node {
58                (source_node.make_component_moniker(), source_node.collection())
59            } else {
60                (self.make_component_moniker(), self.collection())
61            };
62
63            child.reserve_offers(offers.len());
64
65            for offer in offers {
66                if matches!(offer, fdf::Offer::DictionaryOffer(_)) {
67                    has_dictionary_offer = true;
68                }
69
70                match Self::process_node_offer_with_transport_property(
71                    &offer,
72                    source_collection,
73                    &source_name,
74                    !has_manual_service_property,
75                ) {
76                    Ok((processed_offer, property)) => {
77                        child.push_offer(processed_offer);
78                        if let Some(property) = property {
79                            properties.push(property);
80                        }
81                    }
82                    Err(e) => return Err(e),
83                }
84            }
85        }
86
87        child.set_non_composite_properties(properties);
88
89        if let Some(driver_host) = args.driver_host {
90            child.set_driver_host_name_for_colocation(&driver_host);
91        }
92
93        if let Some(symbols) = args.symbols {
94            let mut names = HashSet::new();
95            for symbol in &symbols {
96                if symbol.name.is_none() {
97                    return Err(fdf::NodeError::SymbolNameMissing);
98                }
99                if symbol.address.is_none() {
100                    return Err(fdf::NodeError::SymbolAddressMissing);
101                }
102                if !names.insert(symbol.name.as_ref().unwrap()) {
103                    return Err(fdf::NodeError::SymbolAlreadyExists);
104                }
105            }
106            child.set_symbols(symbols);
107        }
108
109        if let Some(bus_info) = args.bus_info {
110            child.set_bus_info(bus_info);
111        }
112
113        // Copy the subtree dictionary of a parent node down to the child.
114        if let NodeDictionary::Subtree(d) = self.dictionary() {
115            if has_dictionary_offer {
116                panic!("Cannot use dictionary offers on node");
117            }
118
119            child.set_dictionary(NodeDictionary::Subtree(d));
120        }
121
122        let devfs_class_path = args.devfs_args.as_ref().and_then(|args| args.class_name.clone());
123
124        let devfs_connector = if let Some(ref mut devfs_args) = args.devfs_args {
125            let allow_controller = match devfs_args.connector_supports {
126                Some(supports) => supports.contains(fdevfs::ConnectionType::CONTROLLER),
127                _ => false,
128            };
129            let class_name = match (allow_controller, &devfs_args.class_name) {
130                (_, Some(class_name)) => class_name.clone(),
131                (true, None) => format!("No_class_name_but_driver_url_is_{}", self.driver_url()),
132                (_, _) => "Unknown_Class_name".to_string(),
133            };
134
135            child.create_devfs_passthrough(
136                devfs_args.connector.take(),
137                devfs_args.controller_connector.take(),
138                allow_controller,
139                class_name,
140            )
141        } else {
142            child.create_devfs_passthrough(None, None, false, "Unknown_Class_name".to_string())
143        };
144
145        let devfs_device = {
146            let device = self.device();
147            let topological = device.topological.as_ref().unwrap_or_else(|| {
148                panic!("Missing topological devfs node: {}", self.make_topological_path(false))
149            });
150
151            topological
152                .add_child(child.name(), devfs_class_path.as_deref(), devfs_connector)
153                .unwrap_or_else(|_| {
154                    panic!("Failed to export {}", child.make_topological_path(false))
155                })
156        };
157        assert!(devfs_device.topological.is_some());
158        child.set_device(devfs_device);
159
160        if let Some(controller) = controller {
161            let control_handle = child.serve_node_controller(controller);
162            child.set_node_controller(control_handle);
163        }
164
165        if has_dictionary_offer && args.offers_dictionary.is_none() {
166            warn!("cannot have dictionary type offers without supplying the offers_dictionary");
167            return Err(fdf::NodeError::UnsupportedArgs);
168        }
169
170        if !has_dictionary_offer && args.offers_dictionary.is_some() {
171            warn!("supplied offers_dictionary but no offers have Dictionary type.");
172            return Err(fdf::NodeError::UnsupportedArgs);
173        }
174
175        if let Some(offers_dictionary) = args.offers_dictionary {
176            let dictionary_util = self.node_manager.get_dictionary_util().map_err(|e| {
177                error!("failed to get dictionary util: {}", e);
178                fdf::NodeError::Internal
179            })?;
180
181            let dictionary_id =
182                dictionary_util.import_dictionary(offers_dictionary).await.map_err(|e| {
183                    error!("failed to import dictionary: {}", e);
184                    fdf::NodeError::Internal
185                })?;
186
187            let dictionary_offer_services = child
188                .offers()
189                .iter()
190                .filter(|offer| matches!(offer.transport, OfferTransport::Dictionary))
191                .map(|offer| offer.service_name.clone())
192                .collect::<Vec<_>>();
193
194            for dictionary_offer_service in dictionary_offer_services {
195                let dir_connector = dictionary_util
196                    .dictionary_dir_connector_route(dictionary_id, &dictionary_offer_service)
197                    .await
198                    .map_err(|e| {
199                        error!("failed to route dictionary: {}", e);
200                        fdf::NodeError::Internal
201                    })?;
202
203                let mut offers = child.offers();
204                offers
205                    .iter_mut()
206                    .find(|offer| {
207                        matches!(offer.transport, OfferTransport::Dictionary)
208                            && offer.service_name == dictionary_offer_service
209                    })
210                    .unwrap()
211                    .dir_connector = Rc::new(RefCell::new(Some(dir_connector)));
212                child.set_offers(offers);
213            }
214        }
215
216        if let Some(node) = node {
217            let node_server_binding = child.serve_node(node);
218            child.set_state(NodeState::OwnedByParent {
219                node_server_binding: Some(node_server_binding),
220            });
221        } else {
222            // Use a silent bind tracker to avoid tracking binds.
223            let tracker = child.create_bind_result_tracker(true);
224            self.node_manager.bind(&child, tracker);
225        }
226
227        child.add_to_parents();
228
229        Ok(child)
230    }
231
232    async fn wait_for_child_to_exit(&self, name: &str) -> Result<(), fdf::NodeError> {
233        let (sender, receiver) = oneshot::channel();
234        {
235            let child = self.children().into_iter().find(|c| c.name() == name);
236            if let Some(child) = child {
237                if !child.node_shutdown_coordinator.borrow().is_shutting_down() {
238                    return Err(fdf::NodeError::NameAlreadyExists);
239                }
240                child.set_remove_complete_callback(sender);
241                child.node_shutdown_coordinator.borrow_mut().check_node_state();
242            } else {
243                return Ok(());
244            }
245        }
246
247        // Wait for channel
248        receiver.await.map_err(|_| fdf::NodeError::Internal)
249    }
250
251    fn process_node_offer_with_transport_property(
252        add_offer: &fdf::Offer,
253        source_collection: Collection,
254        source_name: &str,
255        generate_service_property: bool,
256    ) -> Result<(NodeOffer, Option<fdf::NodeProperty2>), fdf::NodeError> {
257        let processed_offer = Self::process_node_offer(add_offer, source_collection, source_name)?;
258        let name = &processed_offer.service_name;
259        let property = if generate_service_property && !should_exclude_service(name) {
260            Some(fdf::NodeProperty2 {
261                key: bind_fuchsia::SERVICE.to_string(),
262                value: fdf::NodePropertyValue::StringValue(name.clone()),
263            })
264        } else {
265            None
266        };
267        Ok((processed_offer, property))
268    }
269
270    fn process_node_offer(
271        add_offer: &fdf::Offer,
272        source_collection: Collection,
273        source_name: &str,
274    ) -> Result<NodeOffer, fdf::NodeError> {
275        let (fdecl_offer, transport) = match add_offer {
276            fdf::Offer::ZirconTransport(offer) => (offer, OfferTransport::ZirconTransport),
277            fdf::Offer::DriverTransport(offer) => (offer, OfferTransport::DriverTransport),
278            fdf::Offer::DictionaryOffer(offer) => (offer, OfferTransport::Dictionary),
279            _ => {
280                error!("Unknown offer transport type");
281                return Err(fdf::NodeError::Internal);
282            }
283        };
284
285        let service_offer = match fdecl_offer {
286            fdecl::Offer::Service(service) => service,
287            _ => return Err(fdf::NodeError::UnsupportedArgs),
288        };
289
290        let source_name_from_offer =
291            service_offer.source_name.as_ref().ok_or(fdf::NodeError::OfferSourceNameMissing)?;
292
293        if let Some(target_name) = &service_offer.target_name
294            && target_name != source_name_from_offer
295        {
296            return Err(fdf::NodeError::UnsupportedArgs);
297        }
298
299        if service_offer.source.is_some() || service_offer.target.is_some() {
300            return Err(fdf::NodeError::OfferRefExists);
301        }
302
303        let source_instance_filter = service_offer
304            .source_instance_filter
305            .as_ref()
306            .ok_or(fdf::NodeError::OfferSourceInstanceFilterMissing)?;
307
308        let renamed_instances = service_offer
309            .renamed_instances
310            .as_ref()
311            .ok_or(fdf::NodeError::OfferRenamedInstancesMissing)?;
312
313        // Dictionary based offers don't go to the component framework, but developers can see
314        // these fields in the node list output.
315        let (source_name, source_collection) = match transport {
316            OfferTransport::Dictionary => ("dictionary", Collection::None),
317            _ => (source_name, source_collection),
318        };
319
320        Ok(NodeOffer {
321            source_name: source_name.to_string(),
322            source_collection,
323            transport,
324            service_name: source_name_from_offer.clone(),
325            source_instance_filter: source_instance_filter.clone(),
326            renamed_instances: renamed_instances.clone(),
327            dir_connector: Rc::new(RefCell::new(None)),
328        })
329    }
330
331    fn set_non_composite_properties(&self, properties: Vec<fdf::NodeProperty2>) {
332        self.clear_properties();
333        self.push_property(NodePropertyEntry {
334            name: "default".to_string(),
335            properties: properties.into_iter().map(|p| p.into()).collect(),
336        });
337    }
338}
339
340fn should_exclude_service(service_name: &str) -> bool {
341    if service_name == "fuchsia.driver.compat.Service"
342        || service_name == "fuchsia.hardware.power.PowerTokenService"
343        || service_name == "fuchsia.hardware.interrupt.ControllerRegistryService"
344        || service_name == "fuchsia.hardware.goldfish.ControlService"
345    {
346        return true;
347    }
348    if service_name.contains("Metadata") {
349        return true;
350    }
351    false
352}