Skip to main content

driver_manager_core/
driver_runner.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::bootup_tracker::BootupTracker;
6use crate::driver_host_runner::DriverHostRunner;
7use crate::memory_attribution::MemoryAttributor;
8use crate::offer_injection::OfferInjector;
9use crate::runner::Runner;
10use crate::{DriverRunnerBridge, LoaderServiceFactory, perform_bfs, to_collection};
11use driver_manager_bind::BindManagerHandle;
12use driver_manager_composite::{CompositeNodeSpec, CompositeNodeSpecManager};
13use driver_manager_devfs::Devfs;
14use driver_manager_driver_host::{DriverHost, DriverHostComponent};
15use driver_manager_node::Node;
16use driver_manager_shutdown::NodeRemovalTracker;
17use driver_manager_types::{Collection, to_bind_rule2, to_property2};
18use driver_manager_utils::DictionaryUtil;
19use fidl::endpoints::{ServerEnd, create_endpoints};
20use fuchsia_component::client::connect_to_protocol_at_dir_root;
21use fuchsia_component::server::{ServiceFs, ServiceObjLocal};
22use fuchsia_inspect::ArrayProperty;
23use futures::StreamExt;
24use futures::channel::oneshot;
25use log::{debug, error, info, warn};
26use rand::SeedableRng;
27use rand::rngs::StdRng;
28use std::cell::RefCell;
29use std::collections::HashSet;
30use std::rc::{Rc, Weak};
31use std::sync::Arc;
32use vfs::execution_scope::ExecutionScope;
33use {
34    fidl_fuchsia_component as fcomponent, fidl_fuchsia_component_decl as fdecl,
35    fidl_fuchsia_component_sandbox as fsandbox, fidl_fuchsia_driver_crash as fcrash,
36    fidl_fuchsia_driver_development as fdd, fidl_fuchsia_driver_framework as fdf,
37    fidl_fuchsia_driver_host as fdh, fidl_fuchsia_driver_index as fdi,
38    fidl_fuchsia_driver_token as fdt, fidl_fuchsia_io as fio, fuchsia_async as fasync,
39    fuchsia_inspect as inspect,
40};
41
42pub struct DriverRunner {
43    pub(crate) driver_index: fdi::DriverIndexProxy,
44    pub(crate) dictionary_util: Rc<DictionaryUtil>,
45    loader_service_factory: LoaderServiceFactory,
46    pub(crate) root_node: Rc<Node>,
47    pub bind_manager: BindManagerHandle,
48    pub(crate) composite_node_spec_manager: CompositeNodeSpecManager,
49    runner: Runner,
50    driver_host_runner: Rc<DriverHostRunner>,
51    pub(crate) removal_tracker: Rc<RefCell<NodeRemovalTracker>>,
52    pub bootup_tracker: Rc<BootupTracker>,
53    driver_hosts: RefCell<Vec<Weak<dyn DriverHost>>>,
54    pub devfs: Arc<Devfs>,
55    pub(crate) memory_attributor: Rc<MemoryAttributor>,
56    launcher: Option<fidl_fuchsia_driver_loader::DriverHostLauncherProxy>,
57    pub(crate) enable_test_shutdown_delays: bool,
58    pub(crate) shutdown_test_rng: Rc<RefCell<StdRng>>,
59    pub(crate) scope: fasync::Scope,
60}
61
62impl DriverRunner {
63    #[allow(clippy::too_many_arguments)]
64    pub fn new(
65        realm: fcomponent::RealmProxy,
66        introspector: fcomponent::IntrospectorProxy,
67        dictionary_util: DictionaryUtil,
68        driver_index: fdi::DriverIndexProxy,
69        loader_service_factory: LoaderServiceFactory,
70        enable_test_shutdown_delays: bool,
71        offer_injector: OfferInjector,
72        devfs: Arc<Devfs>,
73    ) -> Rc<Self> {
74        Rc::new_cyclic(|weak_driver_runner| {
75            let bind_manager_bridge = Box::new(DriverRunnerBridge(weak_driver_runner.clone()));
76            let bind_manager = BindManagerHandle::new(bind_manager_bridge);
77
78            let composite_manager_bridge = Box::new(DriverRunnerBridge(weak_driver_runner.clone()));
79            let composite_node_spec_manager =
80                CompositeNodeSpecManager::new(composite_manager_bridge);
81
82            let bootup_tracker = BootupTracker::new(bind_manager.clone());
83
84            let root_node = Node::new(
85                "dev",
86                Weak::new(),
87                Box::new(DriverRunnerBridge(weak_driver_runner.clone())),
88            );
89            root_node.setup_devfs_for_root_node(devfs.root_node());
90
91            let runner = Runner::new(realm.clone(), introspector, offer_injector);
92            let driver_host_runner = DriverHostRunner::new(realm);
93            let removal_tracker = NodeRemovalTracker::new();
94            let memory_attributor = Rc::new(MemoryAttributor::new());
95
96            Self {
97                driver_index,
98                dictionary_util: Rc::new(dictionary_util),
99                loader_service_factory,
100                root_node,
101                bind_manager,
102                composite_node_spec_manager,
103                runner,
104                driver_host_runner,
105                removal_tracker,
106                bootup_tracker,
107                driver_hosts: RefCell::new(Vec::new()),
108                devfs,
109                memory_attributor,
110                launcher: None,
111                enable_test_shutdown_delays,
112                shutdown_test_rng: Rc::new(RefCell::new(StdRng::from_os_rng())),
113                scope: fasync::Scope::new_with_name("driver_runner"),
114            }
115        })
116    }
117
118    pub fn register_notifier(self: &Rc<Self>) -> Result<(), anyhow::Error> {
119        let (client, server) = create_endpoints();
120        self.driver_index.set_notifier(client)?;
121        let weak_self = Rc::downgrade(self);
122        self.scope.spawn_local(async move {
123            let mut stream = server.into_stream();
124            while let Some(Ok(msg)) = stream.next().await {
125                match msg {
126                    fdi::DriverNotifierRequest::NewDriverAvailable { .. } => {
127                        let Some(this) = weak_self.upgrade() else {
128                            return;
129                        };
130                        let _ = this.bind_manager.try_bind_all_available().await;
131                    }
132                }
133            }
134        });
135        Ok(())
136    }
137
138    pub fn root_node(&self) -> Rc<Node> {
139        self.root_node.clone()
140    }
141
142    pub fn get_composite_list_info(&self) -> Vec<fdd::CompositeNodeInfo> {
143        self.composite_node_spec_manager.get_composite_info()
144    }
145
146    pub fn driver_hosts(&self) -> Vec<Rc<dyn DriverHost>> {
147        self.driver_hosts.borrow().iter().filter_map(|w| w.upgrade()).collect()
148    }
149
150    pub fn get_driver_host(
151        &self,
152        driver_host_name_for_colocation: &str,
153    ) -> Option<Rc<dyn DriverHost>> {
154        if driver_host_name_for_colocation.is_empty() {
155            return None;
156        }
157        for host_weak in self.driver_hosts.borrow().iter() {
158            if let Some(host) = host_weak.upgrade()
159                && host.name_for_colocation() == driver_host_name_for_colocation
160            {
161                return Some(host);
162            }
163        }
164        None
165    }
166
167    pub async fn start_root_driver(self: &Rc<Self>, url: String) -> Result<(), zx::Status> {
168        self.bootup_tracker.start();
169        let package_type = if url.starts_with("fuchsia-boot://") {
170            fdf::DriverPackageType::Boot
171        } else {
172            fdf::DriverPackageType::Base
173        };
174
175        let (sender, receiver) = oneshot::channel();
176        self.root_node.set_driver_host_name_for_colocation("root");
177        let self_clone = self.clone();
178        self.scope.spawn_local(async move {
179            if let Err(e) = self_clone.start_driver(&self_clone.root_node, &url, package_type).await
180            {
181                error!("Failed to start root driver: {}", e);
182                sender.send(Err(e)).unwrap();
183            } else {
184                sender.send(Ok(())).unwrap();
185            }
186        });
187        receiver.await.map_err(|_| zx::Status::CANCELED)?
188    }
189
190    pub fn start_devfs_driver(self: &Rc<Self>) {
191        let self_clone = self.clone();
192        self.scope.spawn_local(async move {
193            let (client, server) = fidl::endpoints::create_proxy::<fcomponent::ControllerMarker>();
194            let result = self_clone
195                .runner
196                .create_driver_component(
197                    "devfs_driver",
198                    "fuchsia-boot:///devfs-driver#meta/devfs-driver.cm",
199                    &Collection::Boot.to_string(),
200                    &[],
201                    None,
202                    true,
203                    server,
204                )
205                .await;
206            if let Ok((handle, receiver)) = result {
207                self_clone.devfs.set_component_controller_proxy(client);
208                if let Err(e) = self_clone.devfs.attach_component(handle, receiver).await {
209                    error!("Failed to attach devfs component: {}", e);
210                }
211            } else {
212                error!("Starting the devfs component failed");
213            }
214        });
215    }
216
217    pub async fn start_driver(
218        self: &Rc<Self>,
219        node: &Rc<Node>,
220        url: &str,
221        package_type: fdf::DriverPackageType,
222    ) -> Result<(), zx::Status> {
223        // Ensure `node`'s collection is equal to or higher ranked than its ancestor
224        // nodes' collections. This is to avoid node components having a dependency
225        // cycle with each other. For example, node components in the boot driver
226        // collection depend on the devfs component which ultimately depends on all
227        // components within the package driver collection. If a package driver
228        // component depended on a component in the boot driver collection (a lower
229        // ranked collection than the package driver collection) then a cyclic
230        // dependency would occur.
231        node.set_collection(to_collection(node, package_type));
232        node.set_driver_package_type(package_type);
233
234        let moniker = node.make_component_moniker();
235        self.bootup_tracker.notify_new_start_request(
236            moniker.clone(),
237            url.to_string(),
238            node.weak_from_this(),
239        );
240
241        match self.start_driver_internal(node, url, &moniker).await {
242            Ok(_) => {
243                node.complete_bind(Ok(())).await;
244                self.bootup_tracker.notify_start_complete(&moniker);
245                Ok(())
246            }
247            Err(err) => {
248                node.on_start_error(err);
249                node.complete_bind(Err(err)).await;
250                self.bootup_tracker.notify_start_complete(&moniker);
251                Err(err)
252            }
253        }
254    }
255
256    async fn start_driver_internal(
257        self: &Rc<Self>,
258        node: &Rc<Node>,
259        url: &str,
260        moniker: &str,
261    ) -> Result<(), zx::Status> {
262        let dictionary = node.prepare_dictionary().await;
263
264        if !node.has_component_controller_proxy() {
265            let (client, server) = fidl::endpoints::create_proxy::<fcomponent::ControllerMarker>();
266
267            let offers = node.offers().clone();
268            let create_result = self
269                .runner
270                .create_driver_component(
271                    moniker,
272                    url,
273                    &node.collection().to_string(),
274                    &offers,
275                    dictionary,
276                    node.skip_injected_offers(),
277                    server,
278                )
279                .await;
280
281            match create_result {
282                Ok((handle_info, receiver)) => {
283                    node.set_created_info(client, handle_info, receiver).await;
284                }
285                Err(err) => {
286                    return Err(err);
287                }
288            }
289        }
290
291        node.send_start_request().await?;
292        let (start_info, controller) = node.get_next_start_request().await?;
293        node.start_driver(start_info, controller).await
294    }
295
296    pub async fn create_driver_host(
297        &self,
298        use_next_vdso: bool,
299        driver_host_name_for_colocation: String,
300    ) -> Result<Rc<dyn DriverHost>, zx::Status> {
301        let (exposed_dir_client, exposed_dir_server) = create_endpoints::<fio::DirectoryMarker>();
302        let name = if !driver_host_name_for_colocation.is_empty() {
303            format!("driver-host-{}", driver_host_name_for_colocation.trim_start_matches('#'))
304        } else {
305            format!("driver-host-{}", self.driver_hosts.borrow().len())
306        };
307
308        self.create_driver_host_component(&name, exposed_dir_server, use_next_vdso)?;
309
310        let driver_host_proxy =
311            connect_to_protocol_at_dir_root::<fdh::DriverHostMarker>(&exposed_dir_client)
312                .map_err(|_| zx::Status::INTERNAL)?;
313
314        let (tx, rx) = oneshot::channel();
315        let _ = self.loader_service_factory.unbounded_send(tx);
316        let loader_service_client = rx.await.map_err(|e| {
317            error!("Failed to connect to loader service: {}", e);
318            zx::Status::INTERNAL
319        })??;
320
321        let driver_host: Rc<dyn DriverHost> = Rc::new(DriverHostComponent::new(
322            driver_host_proxy,
323            None,
324            ExecutionScope::new(),
325            driver_host_name_for_colocation,
326        ));
327        driver_host.install_loader(loader_service_client)?;
328
329        self.driver_hosts.borrow_mut().push(Rc::downgrade(&driver_host));
330
331        Ok(driver_host)
332    }
333
334    pub async fn create_driver_host_dynamic_linker(
335        self: &Rc<Self>,
336        driver_host_name_for_colocation: String,
337    ) -> Result<Rc<dyn DriverHost>, zx::Status> {
338        let driver_host_runner = self.driver_host_runner.clone();
339        let launcher = self.launcher.clone().unwrap();
340        let (exposed_dir_client, exposed_dir_server) = create_endpoints::<fio::DirectoryMarker>();
341        let loader_client = driver_host_runner
342            .start_driver_host(launcher, exposed_dir_server)
343            .await
344            .map_err(|e| {
345                error!("Failed to start driver host: {e:?}");
346                zx::Status::INTERNAL
347            })?;
348
349        let driver_host_client =
350            connect_to_protocol_at_dir_root::<fdh::DriverHostMarker>(&exposed_dir_client)
351                .map_err(|_| zx::Status::INTERNAL)?;
352        let driver_host: Rc<dyn DriverHost> = Rc::new(DriverHostComponent::new(
353            driver_host_client,
354            Some(loader_client.into_proxy()),
355            ExecutionScope::new(),
356            driver_host_name_for_colocation,
357        ));
358        self.driver_hosts.borrow_mut().push(Rc::downgrade(&driver_host));
359        Ok(driver_host)
360    }
361
362    fn create_driver_host_component(
363        &self,
364        moniker: &str,
365        exposed_dir: ServerEnd<fio::DirectoryMarker>,
366        use_next_vdso: bool,
367    ) -> Result<(), zx::Status> {
368        let url = if use_next_vdso {
369            "fuchsia-boot:///driver_host#meta/driver_host_next.cm"
370        } else {
371            "fuchsia-boot:///driver_host#meta/driver_host.cm"
372        };
373
374        let child_decl = fdecl::Child {
375            name: Some(moniker.to_string()),
376            url: Some(url.to_string()),
377            startup: Some(fdecl::StartupMode::Lazy),
378            ..Default::default()
379        };
380
381        let create_child_args = fcomponent::CreateChildArgs::default();
382
383        let realm = self.runner.realm.clone();
384        let child_moniker = moniker.to_string();
385        self.scope.spawn_local(async move {
386            let result = realm
387                .create_child(
388                    &fdecl::CollectionRef { name: "driver-hosts".to_string() },
389                    &child_decl,
390                    create_child_args,
391                )
392                .await;
393
394            if let Err(e) = result {
395                error!("Failed to create driver host '{}': {}", child_moniker, e);
396                return;
397            }
398
399            let child_ref = fdecl::ChildRef {
400                name: child_moniker.clone(),
401                collection: Some("driver-hosts".to_string()),
402            };
403            let open_result = realm.open_exposed_dir(&child_ref, exposed_dir).await;
404            if let Err(e) = open_result {
405                error!(
406                    "Failed to open exposed directory for driver host: '{}': {}",
407                    child_moniker, e
408                );
409            }
410        });
411
412        Ok(())
413    }
414
415    pub async fn destroy_driver_host(
416        &self,
417        driver_host_name_for_colocation: String,
418    ) -> Result<(), zx::Status> {
419        let name = if !driver_host_name_for_colocation.is_empty() {
420            let suffix = driver_host_name_for_colocation.trim_start_matches('#');
421            format!("driver-host-{}", suffix)
422        } else {
423            return Err(zx::Status::INVALID_ARGS);
424        };
425
426        let child_ref =
427            fdecl::ChildRef { name: name.clone(), collection: Some("driver-hosts".to_string()) };
428
429        let result = self.runner.realm.destroy_child(&child_ref).await;
430        match result {
431            Ok(Ok(())) => Ok(()),
432            Ok(Err(fcomponent::Error::InstanceNotFound)) => Ok(()),
433            Ok(Err(fcomponent::Error::InstanceDied)) => Ok(()),
434            Ok(Err(e)) => {
435                error!("Failed to destroy driver host '{}': {:?}", name, e);
436                Err(zx::Status::INTERNAL)
437            }
438            Err(e) => {
439                error!("Failed to destroy driver host '{}': {}", name, e);
440                Err(zx::Status::INTERNAL)
441            }
442        }
443    }
444
445    pub fn publish(self: &Rc<Self>, fs: &mut ServiceFs<ServiceObjLocal<'_, ()>>) {
446        self.runner.publish(fs);
447        self.driver_host_runner.publish(fs);
448        self.memory_attributor.publish(fs);
449
450        let this = self.clone();
451        fs.dir("svc").add_fidl_service(move |stream: fdf::CompositeNodeManagerRequestStream| {
452            this.serve_composite_node_manager(stream);
453        });
454
455        let this = self.clone();
456        fs.dir("svc").add_fidl_service(move |stream: fdt::NodeBusTopologyRequestStream| {
457            this.serve_node_bus_topology(stream);
458        });
459
460        let this = self.clone();
461        fs.dir("svc").add_fidl_service(move |stream: fcrash::CrashIntrospectRequestStream| {
462            this.serve_crash_introspect(stream);
463        });
464
465        let this = self.clone();
466        fs.dir("svc").add_fidl_service(move |stream: fdt::DebugRequestStream| {
467            this.serve_debug(stream);
468        });
469    }
470
471    pub fn serve_debug(self: &Rc<Self>, mut stream: fdt::DebugRequestStream) {
472        let this = self.clone();
473        self.scope.spawn_local(async move {
474            while let Some(Ok(request)) = stream.next().await {
475                match request {
476                    fdt::DebugRequest::LogStackTrace { node_token, responder } => {
477                        let result = this.log_stack_trace(node_token).await;
478                        let _ = match result {
479                            Ok(()) => responder.send(Ok(())),
480                            Err(status) => responder.send(Err(status.into_raw())),
481                        };
482                    }
483                    fdt::DebugRequest::GetHostKoid { node_token, responder } => {
484                        let result = this.get_host_koid(node_token).await;
485                        let _ = match result {
486                            Ok(host_koid) => responder.send(Ok(host_koid.raw_koid())),
487                            Err(status) => responder.send(Err(status.into_raw())),
488                        };
489                    }
490                    fdt::DebugRequest::_UnknownMethod { ordinal, .. } => {
491                        warn!("Unknown Debug request: {}", ordinal);
492                    }
493                }
494            }
495        });
496    }
497
498    async fn log_stack_trace(&self, node_token: zx::Event) -> Result<(), zx::Status> {
499        let token_koid = node_token.basic_info()?.koid;
500        let node = self.find_node_by_token_koid(token_koid).await;
501        if let Some(node) = node {
502            if let Some(host) = node.driver_host() {
503                host.trigger_stack_trace();
504                Ok(())
505            } else {
506                Err(zx::Status::NOT_FOUND)
507            }
508        } else {
509            Err(zx::Status::NOT_FOUND)
510        }
511    }
512
513    async fn get_host_koid(&self, node_token: zx::Event) -> Result<zx::Koid, zx::Status> {
514        let token_koid = node_token.basic_info()?.koid;
515        let node = self.find_node_by_token_koid(token_koid).await;
516        if let Some(node) = node {
517            if let Some(host) = node.driver_host() {
518                host.get_process_koid().await
519            } else {
520                Err(zx::Status::NOT_FOUND)
521            }
522        } else {
523            Err(zx::Status::NOT_FOUND)
524        }
525    }
526
527    pub fn serve_composite_node_manager(
528        self: &Rc<Self>,
529        mut stream: fdf::CompositeNodeManagerRequestStream,
530    ) {
531        let this = self.clone();
532        self.scope.spawn_local(async move {
533            while let Some(Ok(request)) = stream.next().await {
534                this.handle_composite_node_manager_request(request).await;
535            }
536        });
537    }
538
539    async fn handle_composite_node_manager_request(
540        self: &Rc<Self>,
541        request: fdf::CompositeNodeManagerRequest,
542    ) {
543        match request {
544            fdf::CompositeNodeManagerRequest::AddSpec { payload, responder } => {
545                let result = self.add_spec(payload).await;
546                let _ = responder.send(result);
547            }
548            fdf::CompositeNodeManagerRequest::_UnknownMethod { .. } => (),
549        }
550    }
551
552    async fn add_spec(
553        self: &Rc<Self>,
554        spec: fdf::CompositeNodeSpec,
555    ) -> Result<(), fdf::CompositeNodeSpecError> {
556        let name = spec.name.clone().ok_or(fdf::CompositeNodeSpecError::MissingArgs)?;
557
558        let parents_present = spec.parents.is_some();
559        let parents2_present = spec.parents2.is_some();
560
561        if !parents_present && !parents2_present {
562            return Err(fdf::CompositeNodeSpecError::MissingArgs);
563        }
564
565        if parents_present && parents2_present {
566            return Err(fdf::CompositeNodeSpecError::DuplicateParents);
567        }
568
569        let parents2 = if let Some(ref parents) = spec.parents {
570            if parents.is_empty() {
571                return Err(fdf::CompositeNodeSpecError::EmptyNodes);
572            }
573            parents
574                .iter()
575                .map(|parent| {
576                    let bind_rules = parent.bind_rules.iter().map(to_bind_rule2).collect();
577                    let properties = parent.properties.iter().map(to_property2).collect();
578                    fdf::ParentSpec2 { bind_rules, properties }
579                })
580                .collect()
581        } else if let Some(ref parents2) = spec.parents2 {
582            if parents2.is_empty() {
583                return Err(fdf::CompositeNodeSpecError::EmptyNodes);
584            }
585            parents2.clone()
586        } else {
587            unreachable!();
588        };
589
590        let driver_host_name_for_colocation = spec.driver_host.clone().unwrap_or_default();
591
592        let spec_for_manager = CompositeNodeSpec::new(
593            name,
594            parents2,
595            Box::new(DriverRunnerBridge(Rc::downgrade(self))),
596            driver_host_name_for_colocation,
597        );
598
599        self.composite_node_spec_manager.add_spec(spec, spec_for_manager).await
600    }
601
602    pub fn serve_node_bus_topology(self: &Rc<Self>, mut stream: fdt::NodeBusTopologyRequestStream) {
603        let this = self.clone();
604        self.scope.spawn_local(async move {
605            while let Some(Ok(request)) = stream.next().await {
606                match request {
607                    fdt::NodeBusTopologyRequest::Get { token, responder } => {
608                        let result = this.get_bus_topology(token).await;
609                        let _ = match result {
610                            Ok(topology) => responder.send(Ok(&topology)),
611                            Err(status) => responder.send(Err(status.into_raw())),
612                        };
613                    }
614                    fdt::NodeBusTopologyRequest::_UnknownMethod { .. } => (),
615                }
616            }
617        });
618    }
619
620    async fn get_bus_topology(&self, token: zx::Event) -> Result<Vec<fdf::BusInfo>, zx::Status> {
621        let token_koid = token.basic_info()?.koid;
622
623        let node = self.find_node_by_token_koid(token_koid).await;
624
625        if let Some(node) = node { Ok(node.get_bus_topology()) } else { Err(zx::Status::NOT_FOUND) }
626    }
627
628    pub fn serve_crash_introspect(
629        self: &Rc<Self>,
630        mut stream: fcrash::CrashIntrospectRequestStream,
631    ) {
632        let this = self.clone();
633        self.scope.spawn_local(async move {
634            while let Some(Ok(request)) = stream.next().await {
635                match request {
636                    fcrash::CrashIntrospectRequest::FindDriverCrash {
637                        process_koid,
638                        thread_koid,
639                        responder,
640                    } => {
641                        let result = this
642                            .find_driver_crash(
643                                zx::Koid::from_raw(process_koid),
644                                zx::Koid::from_raw(thread_koid),
645                            )
646                            .await;
647                        let _ = match result {
648                            Ok(info) => responder.send(Ok(&info)),
649                            Err(status) => responder.send(Err(status.into_raw())),
650                        };
651                    }
652                }
653            }
654        });
655    }
656
657    async fn find_driver_crash(
658        &self,
659        process_koid: zx::Koid,
660        thread_koid: zx::Koid,
661    ) -> Result<fcrash::DriverCrashInfo, zx::Status> {
662        use zx::HandleBased;
663        let hosts = self.driver_hosts.borrow().clone();
664        for host in hosts {
665            if let Some(host) = host.upgrade()
666                && let Ok(koid) = host.get_process_koid().await
667                && koid == process_koid
668            {
669                let crash_info = host.get_crash_info(thread_koid).await?;
670                let token = crash_info.node_token.ok_or(zx::Status::INTERNAL)?;
671                let token_koid = token.into_handle().basic_info()?.koid;
672
673                let node = self.find_node_by_token_koid(token_koid).await;
674
675                if let Some(node) = node {
676                    return Ok(fcrash::DriverCrashInfo {
677                        node_moniker: Some(node.make_component_moniker()),
678                        url: crash_info.url,
679                        ..Default::default()
680                    });
681                } else {
682                    return Err(zx::Status::NOT_FOUND);
683                }
684            }
685        }
686        Err(zx::Status::NOT_FOUND)
687    }
688
689    async fn find_node_by_token_koid(&self, token_koid: zx::Koid) -> Option<Rc<Node>> {
690        let mut result: Option<Rc<Node>> = None;
691        perform_bfs(self.root_node(), async |current| {
692            if result.is_some() {
693                return false; // Already found.
694            }
695            if let Some(current_koid) = current.token_koid()
696                && current_koid == token_koid
697            {
698                result = Some(current.clone());
699                return false;
700            }
701            true
702        })
703        .await;
704        result
705    }
706
707    pub async fn rebind_composites_with_driver(&self, driver_url: &str) -> u32 {
708        let mut names = HashSet::new();
709        perform_bfs(self.root_node(), async |node| {
710            if node.is_composite() && node.driver_url() == driver_url {
711                names.insert(node.name().to_string());
712                return false; // Do not visit children
713            }
714            true // Continue to children
715        })
716        .await;
717
718        let count = names.len() as u32;
719        for name in names {
720            let _ = self.composite_node_spec_manager.rebind(name, None).await;
721        }
722
723        count
724    }
725
726    pub async fn restart_nodes_colocated_with_driver_url(
727        self: &Rc<Self>,
728        url: &str,
729        rematch_flags: fdd::RestartRematchFlags,
730    ) -> Result<u32, zx::Status> {
731        // Step 1: Find all driver hosts with the given driver URL.
732        let mut driver_hosts = HashSet::new();
733        perform_bfs(self.root_node(), async |node| {
734            if node.driver_url() == url
735                && let Some(host) = node.driver_host()
736            {
737                // We need a way to uniquely identify the host.
738                // Using the raw pointer should work.
739                driver_hosts.insert(Rc::as_ptr(&host) as *const ());
740            }
741            true // Continue BFS
742        })
743        .await;
744
745        if driver_hosts.is_empty() {
746            warn!(
747                "restart_nodes_colocated_with_driver_url: no driver hosts found with url {}",
748                url
749            );
750            return Ok(0);
751        }
752
753        let driver_host_count = driver_hosts.len() as u32;
754
755        // Step 2: Perform another BFS to restart the nodes.
756        let this = self.clone();
757        perform_bfs(self.root_node(), async |node| {
758            let host_ptr = node.driver_host().map(|host| Rc::as_ptr(&host) as *const ());
759            if host_ptr.is_none() || !driver_hosts.contains(&host_ptr.unwrap()) {
760                // Not in one of the restarting hosts. Continue to children.
761                return true;
762            }
763
764            // This node is in a driver host that needs to be restarted.
765            if node.evaluate_rematch_flags(rematch_flags, url) {
766                if node.is_composite() {
767                    debug!(
768                        "RestartNodesColocatedWithDriverUrl rebinding composite {}",
769                        node.make_component_moniker()
770                    );
771                    let _ = this
772                        .composite_node_spec_manager
773                        .rebind(node.name().to_string(), None)
774                        .await;
775                } else {
776                    debug!(
777                        "RestartNodesColocatedWithDriverUrl restarting node with rematch {}",
778                        node.make_component_moniker()
779                    );
780                    let (tx, _rx) = oneshot::channel();
781                    node.restart_node_with_rematch(Some("".to_string()), tx);
782                }
783            } else {
784                info!(
785                    "RestartNodesColocatedWithDriverUrl restarting node {}",
786                    node.make_component_moniker()
787                );
788                node.restart_node();
789            }
790
791            false // Do not visit children.
792        })
793        .await;
794
795        Ok(driver_host_count)
796    }
797
798    pub async fn restart_with_dictionary(
799        self: &Rc<Self>,
800        moniker: String,
801        dictionary: fsandbox::DictionaryRef,
802        reset_eventpair: zx::EventPair,
803    ) {
804        let imported = self.dictionary_util.import_dictionary(dictionary).await;
805        let imported = match imported {
806            Ok(imported) => imported,
807            Err(e) => {
808                error!("Failed to import dictionary: {}", e);
809                return;
810            }
811        };
812
813        let mut restarted_node: Option<Rc<Node>> = None;
814        perform_bfs(self.root_node(), async |node| {
815            if restarted_node.is_some() {
816                return false; // Already found, stop searching.
817            }
818
819            if node.make_component_moniker() == moniker {
820                if node.has_subtree_dictionary() {
821                    error!(concat!(
822                        "RestartWithDictionary requested node id already contains a ",
823                        "dictionary_ref from another RestartWithDictionary operation."
824                    ));
825                    return false; // Stop searching
826                }
827                assert!(restarted_node.is_none(), "Multiple nodes with same moniker not possible.");
828                restarted_node = Some(node.clone());
829                node.set_subtree_dictionary(imported);
830                node.restart_node();
831                return false; // Found it, stop searching.
832            }
833
834            true // Continue searching
835        })
836        .await;
837
838        if let Some(restarted_node) = restarted_node {
839            self.scope.spawn_local(async move {
840                let signals = zx::Signals::EVENTPAIR_PEER_CLOSED | zx::Signals::EVENTPAIR_SIGNALED;
841                let on_signals = fasync::OnSignals::new(&reset_eventpair, signals);
842                on_signals.await.expect("failed to wait on eventpair");
843
844                info!("RestartWithDictionary operation released.");
845                restarted_node.remove_subtree_dictionary();
846                restarted_node.restart_node();
847            });
848        }
849    }
850
851    pub fn inspect(&self) -> inspect::Inspector {
852        let inspector =
853            inspect::Inspector::new(inspect::InspectorConfig::default().size(2 * 256 * 1024));
854
855        let mut roots = Vec::new();
856        let mut unique_nodes = HashSet::new();
857
858        let device_tree = inspector.root().create_child("node_topology");
859        let mut root_node_inspect = device_tree.create_child(self.root_node.name());
860
861        self.inspect_node_recursive(
862            &self.root_node,
863            &mut root_node_inspect,
864            &mut roots,
865            &mut unique_nodes,
866        );
867
868        device_tree.record(root_node_inspect);
869        inspector.root().record(device_tree);
870
871        for root in roots {
872            inspector.root().record(root);
873        }
874
875        self.bind_manager.record_inspect(inspector.root());
876
877        inspector
878    }
879
880    fn inspect_node_recursive(
881        &self,
882        node: &Rc<Node>,
883        inspect_node: &mut inspect::Node,
884        roots: &mut Vec<inspect::Node>,
885        unique_nodes: &mut HashSet<*const Node>,
886    ) {
887        let node_ptr = Rc::as_ptr(node);
888        if !unique_nodes.insert(node_ptr) {
889            return;
890        }
891
892        let offers = node.offers();
893        if !offers.is_empty() {
894            let array = inspect_node.create_string_array("offers", offers.len());
895            for (i, offer) in offers.iter().enumerate() {
896                array.set(i, &offer.service_name);
897            }
898            inspect_node.record(array);
899        }
900
901        let symbols = node.symbols();
902        if !symbols.is_empty() {
903            let array = inspect_node.create_string_array("symbols", symbols.len());
904            for (i, symbol) in symbols.iter().enumerate() {
905                if let Some(name) = &symbol.name {
906                    array.set(i, name);
907                }
908            }
909            inspect_node.record(array);
910        }
911
912        if let Some(properties) = node.get_node_properties(None)
913            && !properties.is_empty()
914        {
915            inspect_node.record_child("properties", |properties_node| {
916                for (i, property) in properties.iter().enumerate() {
917                    properties_node.record_child(i.to_string(), |inspect_property| {
918                        inspect_property.record_string("key", &property.key);
919                        match &property.value {
920                            fdf::NodePropertyValue::StringValue(s) => {
921                                inspect_property.record_string("value", s)
922                            }
923                            fdf::NodePropertyValue::IntValue(i) => {
924                                inspect_property.record_uint("value", *i as u64)
925                            }
926                            fdf::NodePropertyValue::EnumValue(e) => {
927                                inspect_property.record_string("value", e)
928                            }
929                            fdf::NodePropertyValue::BoolValue(b) => {
930                                inspect_property.record_bool("value", *b)
931                            }
932                            _ => inspect_property.record_string("value", "UNKNOWN VALUE TYPE"),
933                        }
934                    });
935                }
936            });
937        }
938
939        inspect_node
940            .record_string("type", if node.is_composite() { "Composite Device" } else { "Device" });
941        inspect_node.record_string("topological_path", node.make_topological_path(false));
942        inspect_node.record_string("driver", node.driver_url());
943
944        for child in node.children() {
945            let mut child_inspect_node = inspect_node.create_child(child.name());
946            self.inspect_node_recursive(&child, &mut child_inspect_node, roots, unique_nodes);
947            roots.push(child_inspect_node);
948        }
949    }
950}
951
952#[cfg(test)]
953mod tests {
954    use super::*;
955    use crate::testing::{MockDriverHost, MockNodeManager};
956    use futures::channel::mpsc;
957    use std::sync::atomic::Ordering;
958
959    use fidl_fuchsia_driver_index as fdi;
960
961    #[fasync::run_singlethreaded(test)]
962    async fn test_log_stack_trace() {
963        let (realm, _) = fidl::endpoints::create_proxy::<fcomponent::RealmMarker>();
964        let (introspector, _) = fidl::endpoints::create_proxy::<fcomponent::IntrospectorMarker>();
965        let (driver_index, _) = fidl::endpoints::create_proxy::<fdi::DriverIndexMarker>();
966        let (capability_store, _) =
967            fidl::endpoints::create_proxy::<fsandbox::CapabilityStoreMarker>();
968        let (loader_service_factory, _) = mpsc::unbounded();
969
970        let driver_runner = DriverRunner::new(
971            realm,
972            introspector,
973            DictionaryUtil::new(capability_store),
974            driver_index,
975            loader_service_factory,
976            false,
977            OfferInjector::new(crate::offer_injection::PowerOffersConfig {
978                power_inject_offer: false,
979                power_suspend_enabled: false,
980            }),
981            Devfs::new(mpsc::unbounded().0),
982        );
983
984        let node_manager = Box::new(MockNodeManager);
985        let node = Node::new("test_node", Rc::downgrade(&driver_runner.root_node), node_manager);
986        let host = Rc::new(MockDriverHost::new());
987        node.set_host(host.clone());
988
989        // We need the node to have a token and be found by find_node_by_token_koid.
990        // Node::token_koid() returns Some(koid) if it's in DriverComponent state.
991        let token = zx::Event::create();
992        let token_remote = token.duplicate(zx::Rights::SAME_RIGHTS).unwrap();
993        let koid = token.koid().unwrap();
994
995        node.set_state_for_testing(driver_manager_node::types::NodeState::DriverComponent(
996            driver_manager_node::types::DriverComponent::new(
997                "url".to_string(),
998                token_remote,
999                koid,
1000                None,
1001                None,
1002                None,
1003                driver_manager_node::types::DriverState::Running,
1004            ),
1005        ));
1006
1007        // Add the node to the driver runner's root node as a child.
1008        driver_runner.root_node.add_child_to_children_for_testing(node);
1009
1010        // Call LogStackTrace with the token.
1011        let result = driver_runner.log_stack_trace(token).await;
1012        assert!(result.is_ok());
1013        assert_eq!(host.stack_trace_count.load(Ordering::SeqCst), 1);
1014
1015        // Call with a random token should return NOT_FOUND.
1016        let random_token = zx::Event::create();
1017        let result = driver_runner.log_stack_trace(random_token).await;
1018        assert_eq!(result, Err(zx::Status::NOT_FOUND));
1019    }
1020
1021    #[fasync::run_singlethreaded(test)]
1022    async fn test_get_host_koid() {
1023        let (realm, _) = fidl::endpoints::create_proxy::<fcomponent::RealmMarker>();
1024        let (introspector, _) = fidl::endpoints::create_proxy::<fcomponent::IntrospectorMarker>();
1025        let (driver_index, _) = fidl::endpoints::create_proxy::<fdi::DriverIndexMarker>();
1026        let (capability_store, _) =
1027            fidl::endpoints::create_proxy::<fsandbox::CapabilityStoreMarker>();
1028        let (loader_service_factory, _) = mpsc::unbounded();
1029
1030        let driver_runner = DriverRunner::new(
1031            realm,
1032            introspector,
1033            DictionaryUtil::new(capability_store),
1034            driver_index,
1035            loader_service_factory,
1036            false,
1037            OfferInjector::new(crate::offer_injection::PowerOffersConfig {
1038                power_inject_offer: false,
1039                power_suspend_enabled: false,
1040            }),
1041            Devfs::new(mpsc::unbounded().0),
1042        );
1043
1044        let node_manager = Box::new(MockNodeManager);
1045        let node = Node::new("test_node", Rc::downgrade(&driver_runner.root_node), node_manager);
1046        let host = Rc::new(MockDriverHost::new());
1047        node.set_host(host.clone());
1048
1049        let token = zx::Event::create();
1050        let token_remote = token.duplicate(zx::Rights::SAME_RIGHTS).unwrap();
1051        let koid = token.koid().unwrap();
1052
1053        node.set_state_for_testing(driver_manager_node::types::NodeState::DriverComponent(
1054            driver_manager_node::types::DriverComponent::new(
1055                "url".to_string(),
1056                token_remote,
1057                koid,
1058                None,
1059                None,
1060                None,
1061                driver_manager_node::types::DriverState::Running,
1062            ),
1063        ));
1064
1065        driver_runner.root_node.add_child_to_children_for_testing(node);
1066
1067        let result = driver_runner.get_host_koid(token).await;
1068        assert_eq!(result, Ok(zx::Koid::from_raw(0)));
1069
1070        let random_token = zx::Event::create();
1071        let result = driver_runner.get_host_koid(random_token).await;
1072        assert_eq!(result, Err(zx::Status::NOT_FOUND));
1073    }
1074}