1use 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 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 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 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 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}