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