1use crate::bedrock::aggregate_router::{AggregateRouterFn, AggregateSource};
6use crate::bedrock::request_metadata::Metadata;
7use crate::bedrock::structured_dict::{
8 ComponentEnvironment, ComponentInput, ComponentOutput, StructuredDictMap,
9};
10use crate::bedrock::use_dictionary_router::UseDictionaryRouter;
11use crate::bedrock::with_service_renames_and_filter::WithServiceRenamesAndFilter;
12use crate::capability_source::{
13 AggregateCapability, AggregateInstance, AggregateMember, AnonymizedAggregateSource,
14 CapabilitySource, ComponentCapability, ComponentSource, FilteredAggregateProviderSource,
15 InternalCapability, InternalEventStreamCapability, VoidSource,
16};
17use crate::component_instance::{ComponentInstanceInterface, WeakComponentInstanceInterface};
18use crate::error::{ErrorReporter, RouteRequestErrorInfo, RoutingError};
19use crate::{DictExt, LazyGet, WithPorcelain};
20use async_trait::async_trait;
21use cm_rust::offer::{OfferDecl, OfferDeclCommon};
22use cm_rust::{
23 CapabilityTypeName, DictionaryValue, ExposeDecl, ExposeDeclCommon, NativeIntoFidl, SourceName,
24 SourcePath, UseDeclCommon,
25};
26use cm_types::{Availability, BorrowedSeparatedPath, IterablePath, Name, SeparatedPath};
27use fidl::endpoints::DiscoverableProtocolMarker;
28use fidl_fuchsia_component as fcomponent;
29use fidl_fuchsia_component_decl as fdecl;
30use fidl_fuchsia_component_internal as finternal;
31use fidl_fuchsia_io as fio;
32use fidl_fuchsia_sys2 as fsys;
33use fuchsia_sync::Mutex;
34use futures::FutureExt;
35use itertools::Itertools;
36use log::warn;
37use moniker::{ChildName, Moniker};
38use router_error::RouterError;
39use runtime_capabilities::{
40 Capability, CapabilityBound, Connector, Data, Dictionary, DirConnector, Request, Routable,
41 Router, WeakInstanceToken,
42};
43use std::collections::{BTreeMap, HashMap};
44use std::fmt::Debug;
45use std::sync::{Arc, LazyLock};
46
47pub type EventStreamFilter = Option<BTreeMap<String, DictionaryValue>>;
49
50#[derive(Clone)]
52pub struct EventStreamSourceRouter {
53 pub router: Router<Dictionary>,
56 pub filter: EventStreamFilter,
59}
60pub type EventStreamUseRouterFn<C> =
61 dyn Fn(&Arc<C>, Vec<EventStreamSourceRouter>) -> Router<Connector>;
62
63static NAMESPACE: LazyLock<Name> = LazyLock::new(|| "namespace".parse().unwrap());
64static NUMBERED_HANDLES: LazyLock<Name> = LazyLock::new(|| "numbered_handles".parse().unwrap());
65static RUNNER: LazyLock<Name> = LazyLock::new(|| "runner".parse().unwrap());
66static CONFIG: LazyLock<Name> = LazyLock::new(|| "config".parse().unwrap());
67
68#[derive(Debug, Clone)]
70pub struct ProgramInput {
71 inner: Dictionary,
77}
78
79impl Default for ProgramInput {
80 fn default() -> Self {
81 Self::new(Dictionary::new(), None, Dictionary::new())
82 }
83}
84
85impl From<ProgramInput> for Dictionary {
86 fn from(program_input: ProgramInput) -> Self {
87 program_input.inner
88 }
89}
90
91impl ProgramInput {
92 pub fn new(
93 namespace: Dictionary,
94 runner: Option<Router<Connector>>,
95 config: Dictionary,
96 ) -> Self {
97 let inner = Dictionary::new();
98 inner.insert(NAMESPACE.clone(), namespace.into()).unwrap();
99 if let Some(runner) = runner {
100 inner.insert(RUNNER.clone(), runner.into()).unwrap();
101 }
102 inner.insert(NUMBERED_HANDLES.clone(), Dictionary::new().into()).unwrap();
103 inner.insert(CONFIG.clone(), config.into()).unwrap();
104 ProgramInput { inner }
105 }
106
107 pub fn namespace(&self) -> Dictionary {
109 let cap = self.inner.get(&*NAMESPACE).unwrap();
110 let Capability::Dictionary(dict) = cap else {
111 unreachable!("namespace entry must be a dictionary: {cap:?}");
112 };
113 dict
114 }
115
116 pub fn numbered_handles(&self) -> Dictionary {
118 let cap = self.inner.get(&*NUMBERED_HANDLES).unwrap();
119 let Capability::Dictionary(dict) = cap else {
120 unreachable!("numbered_handles entry must be a dictionary: {cap:?}");
121 };
122 dict
123 }
124
125 pub fn runner(&self) -> Option<Router<Connector>> {
127 let cap = self.inner.get(&*RUNNER);
128 match cap {
129 None => None,
130 Some(Capability::ConnectorRouter(r)) => Some(r),
131 cap => unreachable!("runner entry must be a router: {cap:?}"),
132 }
133 }
134
135 fn set_runner(&self, capability: Capability) {
136 self.inner.insert(RUNNER.clone(), capability).unwrap()
137 }
138
139 pub fn config(&self) -> Dictionary {
141 let cap = self.inner.get(&*CONFIG).unwrap();
142 let Capability::Dictionary(dict) = cap else {
143 unreachable!("config entry must be a dictionary: {cap:?}");
144 };
145 dict
146 }
147}
148
149#[derive(Debug)]
152pub struct ComponentSandbox {
153 pub component_input: ComponentInput,
155
156 pub component_output: ComponentOutput,
158
159 pub program_input: ProgramInput,
161
162 pub program_output_dict: Dictionary,
164
165 framework_router: Mutex<Router<Dictionary>>,
175
176 pub capability_sourced_capabilities_dict: Dictionary,
179
180 pub declared_dictionaries: Dictionary,
182
183 pub child_inputs: StructuredDictMap<ComponentInput>,
186
187 pub collection_inputs: StructuredDictMap<ComponentInput>,
191}
192
193impl Default for ComponentSandbox {
194 fn default() -> Self {
195 static NULL_ROUTER: LazyLock<Router<Dictionary>> =
196 LazyLock::new(|| Router::new(NullRouter {}));
197 struct NullRouter;
198 #[async_trait]
199 impl Routable<Dictionary> for NullRouter {
200 async fn route(
201 &self,
202 _request: Option<Request>,
203 _target: WeakInstanceToken,
204 ) -> Result<Option<Dictionary>, RouterError> {
205 panic!("null router invoked");
206 }
207 async fn route_debug(
208 &self,
209 _request: Option<Request>,
210 _target: WeakInstanceToken,
211 ) -> Result<Data, RouterError> {
212 panic!("null router invoked");
213 }
214 }
215 let framework_router = Mutex::new(NULL_ROUTER.clone());
216 Self {
217 framework_router,
218 component_input: Default::default(),
219 component_output: Default::default(),
220 program_input: Default::default(),
221 program_output_dict: Default::default(),
222 capability_sourced_capabilities_dict: Default::default(),
223 declared_dictionaries: Default::default(),
224 child_inputs: Default::default(),
225 collection_inputs: Default::default(),
226 }
227 }
228}
229
230impl From<ComponentSandbox> for Dictionary {
231 fn from(sandbox: ComponentSandbox) -> Dictionary {
232 let sandbox_dictionary = Dictionary::new();
233 sandbox_dictionary
234 .insert(Name::new("framework").unwrap(), sandbox.framework_router.lock().clone().into())
235 .unwrap();
236 sandbox_dictionary
237 .insert(
238 Name::new("component_input").unwrap(),
239 Capability::Dictionary(sandbox.component_input.into()),
240 )
241 .unwrap();
242 sandbox_dictionary
243 .insert(
244 Name::new("component_output").unwrap(),
245 Capability::Dictionary(sandbox.component_output.into()),
246 )
247 .unwrap();
248 sandbox_dictionary
249 .insert(
250 Name::new("program_input").unwrap(),
251 Capability::Dictionary(sandbox.program_input.into()),
252 )
253 .unwrap();
254 sandbox_dictionary
255 .insert(Name::new("program_output").unwrap(), sandbox.program_output_dict.into())
256 .unwrap();
257 sandbox_dictionary
258 .insert(
259 Name::new("capability_sourced").unwrap(),
260 sandbox.capability_sourced_capabilities_dict.into(),
261 )
262 .unwrap();
263 sandbox_dictionary
264 .insert(
265 Name::new("declared_dictionaries").unwrap(),
266 sandbox.declared_dictionaries.into(),
267 )
268 .unwrap();
269 sandbox_dictionary
270 .insert(
271 Name::new("child_inputs").unwrap(),
272 Capability::Dictionary(sandbox.child_inputs.into()),
273 )
274 .unwrap();
275 sandbox_dictionary
276 .insert(
277 Name::new("collection_inputs").unwrap(),
278 Capability::Dictionary(sandbox.collection_inputs.into()),
279 )
280 .unwrap();
281 sandbox_dictionary
282 }
283}
284
285impl Clone for ComponentSandbox {
286 fn clone(&self) -> Self {
287 let Self {
288 component_input,
289 component_output,
290 program_input,
291 program_output_dict,
292 framework_router,
293 capability_sourced_capabilities_dict,
294 declared_dictionaries,
295 child_inputs,
296 collection_inputs,
297 } = self;
298 Self {
299 component_input: component_input.clone(),
300 component_output: component_output.clone(),
301 program_input: program_input.clone(),
302 program_output_dict: program_output_dict.clone(),
303 framework_router: Mutex::new(framework_router.lock().clone()),
304 capability_sourced_capabilities_dict: capability_sourced_capabilities_dict.clone(),
305 declared_dictionaries: declared_dictionaries.clone(),
306 child_inputs: child_inputs.clone(),
307 collection_inputs: collection_inputs.clone(),
308 }
309 }
310}
311
312impl ComponentSandbox {
313 pub fn append(&self, sandbox: &ComponentSandbox) {
316 let ComponentSandbox {
319 component_input,
320 component_output,
321 program_input,
322 program_output_dict,
323 framework_router,
324 capability_sourced_capabilities_dict,
325 declared_dictionaries,
326 child_inputs,
327 collection_inputs,
328 } = sandbox;
329 for (copy_from, copy_to) in [
330 (&component_input.capabilities(), &self.component_input.capabilities()),
331 (&component_input.environment().debug(), &self.component_input.environment().debug()),
332 (
333 &component_input.environment().runners(),
334 &self.component_input.environment().runners(),
335 ),
336 (
337 &component_input.environment().resolvers(),
338 &self.component_input.environment().resolvers(),
339 ),
340 (&component_output.capabilities(), &self.component_output.capabilities()),
341 (&component_output.framework(), &self.component_output.framework()),
342 (&program_input.namespace(), &self.program_input.namespace()),
343 (&program_input.numbered_handles(), &self.program_input.numbered_handles()),
344 (&program_input.config(), &self.program_input.config()),
345 (&program_output_dict, &self.program_output_dict),
346 (&capability_sourced_capabilities_dict, &self.capability_sourced_capabilities_dict),
347 (&declared_dictionaries, &self.declared_dictionaries),
348 ] {
349 copy_to.append(copy_from).expect("conflicting entry found");
350 }
351 if let Some(timeout) = component_input.environment().stop_timeout() {
352 self.component_input.environment().set_stop_timeout(timeout as i64);
353 }
354 *self.framework_router.lock() = framework_router.lock().clone();
355 if let Some(runner_router) = program_input.runner() {
356 self.program_input.set_runner(runner_router.into());
357 }
358 self.child_inputs.append(child_inputs).unwrap();
359 self.collection_inputs.append(collection_inputs).unwrap();
360 }
361
362 pub fn framework_router(&self) -> Router<Dictionary> {
363 self.framework_router.lock().clone()
364 }
365}
366
367pub fn build_component_sandbox<C: ComponentInstanceInterface + 'static>(
370 component: &Arc<C>,
371 child_component_output_dictionary_routers: HashMap<ChildName, Router<Dictionary>>,
372 decl: &cm_rust::ComponentDecl,
373 component_input: ComponentInput,
374 program_output_dict: Dictionary,
375 framework_router: Router<Dictionary>,
376 capability_sourced_capabilities_dict: Dictionary,
377 declared_dictionaries: Dictionary,
378 error_reporter: impl ErrorReporter,
379 aggregate_router_fn: &AggregateRouterFn<C>,
380 event_stream_use_router_fn: &EventStreamUseRouterFn<C>,
381) -> ComponentSandbox {
382 let component_output = ComponentOutput::new();
383 let program_input = ProgramInput::default();
384 let environments: StructuredDictMap<ComponentEnvironment> = Default::default();
385 let child_inputs: StructuredDictMap<ComponentInput> = Default::default();
386 let collection_inputs: StructuredDictMap<ComponentInput> = Default::default();
387
388 for environment_decl in &decl.environments {
389 environments
390 .insert(
391 environment_decl.name.clone(),
392 build_environment(
393 component,
394 &child_component_output_dictionary_routers,
395 &component_input,
396 environment_decl,
397 &program_output_dict,
398 &error_reporter,
399 ),
400 )
401 .ok();
402 }
403
404 for child in &decl.children {
405 let environment;
406 if let Some(environment_name) = child.environment.as_ref() {
407 environment = environments.get(environment_name).expect(
408 "child references nonexistent environment, \
409 this should be prevented in manifest validation",
410 );
411 } else {
412 environment = component_input.environment();
413 }
414 let input = ComponentInput::new(environment);
415 let name = Name::new(child.name.as_str()).expect("child is static so name is not long");
416 child_inputs.insert(name, input).ok();
417 }
418
419 for collection in &decl.collections {
420 let environment;
421 if let Some(environment_name) = collection.environment.as_ref() {
422 environment = environments.get(environment_name).expect(
423 "collection references nonexistent environment, \
424 this should be prevented in manifest validation",
425 )
426 } else {
427 environment = component_input.environment();
428 }
429 let input = ComponentInput::new(environment);
430 collection_inputs.insert(collection.name.clone(), input).ok();
431 }
432
433 let mut dictionary_use_bundles = Vec::with_capacity(decl.uses.len());
434 for use_bundle in group_use_aggregates(&decl.uses).into_iter() {
435 let first_use = *use_bundle.first().unwrap();
436 match first_use {
437 cm_rust::UseDecl::Service(_)
438 if matches!(first_use.source(), cm_rust::UseSource::Collection(_)) =>
439 {
440 let cm_rust::UseSource::Collection(collection_name) = first_use.source() else {
441 unreachable!();
442 };
443 let availability = *first_use.availability();
444 let aggregate = (aggregate_router_fn)(
445 component.clone(),
446 vec![AggregateSource::Collection { collection_name: collection_name.clone() }],
447 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
448 capability: AggregateCapability::Service(first_use.source_name().clone()),
449 moniker: component.moniker().clone(),
450 members: vec![AggregateMember::try_from(first_use).unwrap()],
451 instances: vec![],
452 }),
453 )
454 .with_porcelain_with_default(CapabilityTypeName::Service)
455 .availability(availability)
456 .target(component)
457 .error_info(first_use)
458 .error_reporter(error_reporter.clone())
459 .build();
460 if let Err(e) = program_input
461 .namespace()
462 .insert_capability(first_use.path().unwrap(), aggregate.into())
463 {
464 warn!(
465 "failed to insert {} in program input dictionary: {e:?}",
466 first_use.path().unwrap()
467 )
468 }
469 }
470 cm_rust::UseDecl::Service(_) => extend_dict_with_use::<DirConnector, _>(
471 component,
472 &child_component_output_dictionary_routers,
473 &component_input,
474 &program_input,
475 &program_output_dict,
476 &framework_router,
477 &capability_sourced_capabilities_dict,
478 first_use,
479 error_reporter.clone(),
480 ),
481 cm_rust::UseDecl::Directory(_) | cm_rust::UseDecl::Storage(_) => {
482 extend_dict_with_use::<DirConnector, C>(
483 component,
484 &child_component_output_dictionary_routers,
485 &component_input,
486 &program_input,
487 &program_output_dict,
488 &framework_router,
489 &capability_sourced_capabilities_dict,
490 first_use,
491 error_reporter.clone(),
492 )
493 }
494 cm_rust::UseDecl::Protocol(_) | cm_rust::UseDecl::Runner(_) => {
495 extend_dict_with_use::<Connector, _>(
496 component,
497 &child_component_output_dictionary_routers,
498 &component_input,
499 &program_input,
500 &program_output_dict,
501 &framework_router,
502 &capability_sourced_capabilities_dict,
503 first_use,
504 error_reporter.clone(),
505 )
506 }
507 cm_rust::UseDecl::Config(config) => extend_dict_with_config_use(
508 component,
509 &child_component_output_dictionary_routers,
510 &component_input,
511 &program_input,
512 &program_output_dict,
513 config,
514 error_reporter.clone(),
515 ),
516 cm_rust::UseDecl::EventStream(_) => extend_dict_with_event_stream_uses(
517 component,
518 &component_input,
519 &program_input,
520 use_bundle,
521 error_reporter.clone(),
522 event_stream_use_router_fn,
523 ),
524 cm_rust::UseDecl::Dictionary(_) => {
525 dictionary_use_bundles.push(use_bundle);
526 }
527 }
528 }
529
530 if !decl.uses.iter().any(|u| matches!(u, cm_rust::UseDecl::Runner(_))) {
534 if let Some(runner_name) = decl.program.as_ref().and_then(|p| p.runner.as_ref()) {
535 extend_dict_with_use::<Connector, _>(
536 component,
537 &child_component_output_dictionary_routers,
538 &component_input,
539 &program_input,
540 &program_output_dict,
541 &framework_router,
542 &capability_sourced_capabilities_dict,
543 &cm_rust::UseDecl::Runner(cm_rust::UseRunnerDecl {
544 source: cm_rust::UseSource::Environment,
545 source_name: runner_name.clone(),
546 source_dictionary: Default::default(),
547 }),
548 error_reporter.clone(),
549 )
550 }
551 }
552
553 for dictionary_use_bundle in dictionary_use_bundles {
560 extend_dict_with_dictionary_use(
561 component,
562 &child_component_output_dictionary_routers,
563 &component_input,
564 &program_input,
565 &program_output_dict,
566 &framework_router,
567 &capability_sourced_capabilities_dict,
568 dictionary_use_bundle,
569 error_reporter.clone(),
570 )
571 }
572
573 for offer_bundle in group_offer_aggregates(&decl.offers) {
574 let first_offer = offer_bundle.first().unwrap();
575 let get_target_dict = || match first_offer.target() {
576 cm_rust::offer::OfferTarget::Child(child_ref) => {
577 assert!(child_ref.collection.is_none(), "unexpected dynamic offer target");
578 let child_name = Name::new(child_ref.name.as_str())
579 .expect("child is static so name is not long");
580 if child_inputs.get(&child_name).is_none() {
581 child_inputs.insert(child_name.clone(), Default::default()).ok();
582 }
583 child_inputs
584 .get(&child_name)
585 .expect("component input was just added")
586 .capabilities()
587 }
588 cm_rust::offer::OfferTarget::Collection(name) => {
589 if collection_inputs.get(&name).is_none() {
590 collection_inputs.insert(name.clone(), Default::default()).ok();
591 }
592 collection_inputs
593 .get(&name)
594 .expect("collection input was just added")
595 .capabilities()
596 }
597 cm_rust::offer::OfferTarget::Capability(name) => {
598 let dict = match declared_dictionaries.get(name) {
599 Some(dict) => dict,
600 None => {
601 let dict = Dictionary::new();
602 declared_dictionaries
603 .insert(name.clone(), Capability::Dictionary(dict.clone()))
604 .ok();
605 Capability::Dictionary(dict)
606 }
607 };
608 let Capability::Dictionary(dict) = dict else {
609 panic!("wrong type in dictionary");
610 };
611 dict
612 }
613 };
614 match first_offer {
615 cm_rust::offer::OfferDecl::Service(_)
616 if offer_bundle.len() == 1
617 && !matches!(
618 first_offer.source(),
619 cm_rust::offer::OfferSource::Collection(_)
620 ) =>
621 {
622 extend_dict_with_offer::<DirConnector, _>(
623 component,
624 &child_component_output_dictionary_routers,
625 &component_input,
626 &program_output_dict,
627 &framework_router,
628 &capability_sourced_capabilities_dict,
629 first_offer,
630 &(get_target_dict)(),
631 error_reporter.clone(),
632 )
633 }
634 cm_rust::offer::OfferDecl::Service(_) => {
635 let aggregate_router = new_aggregate_router_from_service_offers(
636 &offer_bundle,
637 component,
638 &child_component_output_dictionary_routers,
639 &component_input,
640 &program_output_dict,
641 &framework_router,
642 &capability_sourced_capabilities_dict,
643 error_reporter.clone(),
644 aggregate_router_fn,
645 );
646 (get_target_dict)()
647 .insert(first_offer.target_name().clone(), aggregate_router.into())
648 .expect("failed to insert capability into target dict");
649 }
650 cm_rust::offer::OfferDecl::Config(_) => extend_dict_with_offer::<Data, _>(
651 component,
652 &child_component_output_dictionary_routers,
653 &component_input,
654 &program_output_dict,
655 &framework_router,
656 &capability_sourced_capabilities_dict,
657 first_offer,
658 &(get_target_dict)(),
659 error_reporter.clone(),
660 ),
661 cm_rust::offer::OfferDecl::Directory(_) | cm_rust::offer::OfferDecl::Storage(_) => {
662 extend_dict_with_offer::<DirConnector, _>(
663 component,
664 &child_component_output_dictionary_routers,
665 &component_input,
666 &program_output_dict,
667 &framework_router,
668 &capability_sourced_capabilities_dict,
669 first_offer,
670 &(get_target_dict)(),
671 error_reporter.clone(),
672 )
673 }
674 cm_rust::offer::OfferDecl::Dictionary(_)
675 | cm_rust::offer::OfferDecl::EventStream(_) => extend_dict_with_offer::<Dictionary, _>(
676 component,
677 &child_component_output_dictionary_routers,
678 &component_input,
679 &program_output_dict,
680 &framework_router,
681 &capability_sourced_capabilities_dict,
682 first_offer,
683 &(get_target_dict)(),
684 error_reporter.clone(),
685 ),
686 cm_rust::offer::OfferDecl::Protocol(_)
687 | cm_rust::offer::OfferDecl::Runner(_)
688 | cm_rust::offer::OfferDecl::Resolver(_) => extend_dict_with_offer::<Connector, _>(
689 component,
690 &child_component_output_dictionary_routers,
691 &component_input,
692 &program_output_dict,
693 &framework_router,
694 &capability_sourced_capabilities_dict,
695 first_offer,
696 &(get_target_dict)(),
697 error_reporter.clone(),
698 ),
699 }
700 }
701
702 for expose_bundle in group_expose_aggregates(&decl.exposes) {
703 let first_expose = expose_bundle.first().unwrap();
704 match first_expose {
705 cm_rust::ExposeDecl::Service(_)
706 if expose_bundle.len() == 1
707 && !matches!(first_expose.source(), cm_rust::ExposeSource::Collection(_)) =>
708 {
709 extend_dict_with_expose::<DirConnector, _>(
710 component,
711 &child_component_output_dictionary_routers,
712 &program_output_dict,
713 &framework_router,
714 &capability_sourced_capabilities_dict,
715 first_expose,
716 &component_output,
717 error_reporter.clone(),
718 )
719 }
720 cm_rust::ExposeDecl::Service(_) => {
721 let mut aggregate_sources = vec![];
722 let temp_component_output = ComponentOutput::new();
723 for expose in expose_bundle.iter() {
724 extend_dict_with_expose::<DirConnector, _>(
725 component,
726 &child_component_output_dictionary_routers,
727 &program_output_dict,
728 &framework_router,
729 &capability_sourced_capabilities_dict,
730 expose,
731 &temp_component_output,
732 error_reporter.clone(),
733 );
734 match temp_component_output.capabilities().remove(first_expose.target_name()) {
735 Some(Capability::DirConnectorRouter(router)) => {
736 let source_instance = match expose.source() {
737 cm_rust::ExposeSource::Self_ => AggregateInstance::Self_,
738 cm_rust::ExposeSource::Child(name) => AggregateInstance::Child(
739 moniker::ChildName::new(name.clone().to_long(), None),
740 ),
741 other_source => {
742 warn!(
743 "unsupported source found in expose aggregate: {:?}",
744 other_source
745 );
746 continue;
747 }
748 };
749 aggregate_sources
750 .push(AggregateSource::DirectoryRouter { source_instance, router })
751 }
752 None => match expose.source() {
753 cm_rust::ExposeSource::Collection(collection_name) => {
754 aggregate_sources.push(AggregateSource::Collection {
755 collection_name: collection_name.clone(),
756 });
757 }
758 _ => continue,
759 },
760 other_value => panic!("unexpected dictionary entry: {:?}", other_value),
761 }
762 }
763 let availability = *first_expose.availability();
764 let aggregate = (aggregate_router_fn)(
765 component.clone(),
766 aggregate_sources,
767 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
768 capability: AggregateCapability::Service(
769 first_expose.target_name().clone(),
770 ),
771 moniker: component.moniker().clone(),
772 members: expose_bundle
773 .iter()
774 .filter_map(|e| AggregateMember::try_from(*e).ok())
775 .collect(),
776 instances: vec![],
777 }),
778 );
779 let router = aggregate
780 .with_porcelain_with_default(CapabilityTypeName::Service)
781 .availability(availability)
782 .target(component)
783 .error_info(*first_expose)
784 .error_reporter(error_reporter.clone())
785 .build();
786 component_output
787 .capabilities()
788 .insert(first_expose.target_name().clone(), router.into())
789 .expect("failed to insert capability into target dict")
790 }
791 cm_rust::ExposeDecl::Config(_) => extend_dict_with_expose::<Data, _>(
792 component,
793 &child_component_output_dictionary_routers,
794 &program_output_dict,
795 &framework_router,
796 &capability_sourced_capabilities_dict,
797 first_expose,
798 &component_output,
799 error_reporter.clone(),
800 ),
801 cm_rust::ExposeDecl::Dictionary(_) => extend_dict_with_expose::<Dictionary, _>(
802 component,
803 &child_component_output_dictionary_routers,
804 &program_output_dict,
805 &framework_router,
806 &capability_sourced_capabilities_dict,
807 first_expose,
808 &component_output,
809 error_reporter.clone(),
810 ),
811 cm_rust::ExposeDecl::Directory(_) => extend_dict_with_expose::<DirConnector, _>(
812 component,
813 &child_component_output_dictionary_routers,
814 &program_output_dict,
815 &framework_router,
816 &capability_sourced_capabilities_dict,
817 first_expose,
818 &component_output,
819 error_reporter.clone(),
820 ),
821 cm_rust::ExposeDecl::Protocol(_)
822 | cm_rust::ExposeDecl::Runner(_)
823 | cm_rust::ExposeDecl::Resolver(_) => extend_dict_with_expose::<Connector, _>(
824 component,
825 &child_component_output_dictionary_routers,
826 &program_output_dict,
827 &framework_router,
828 &capability_sourced_capabilities_dict,
829 first_expose,
830 &component_output,
831 error_reporter.clone(),
832 ),
833 }
834 }
835
836 ComponentSandbox {
837 component_input,
838 component_output,
839 program_input,
840 program_output_dict,
841 framework_router: Mutex::new(framework_router),
842 capability_sourced_capabilities_dict,
843 declared_dictionaries,
844 child_inputs,
845 collection_inputs,
846 }
847}
848
849fn new_aggregate_router_from_service_offers<C: ComponentInstanceInterface + 'static>(
850 offer_bundle: &Vec<&cm_rust::offer::OfferDecl>,
851 component: &Arc<C>,
852 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
853 component_input: &ComponentInput,
854 program_output_dict: &Dictionary,
855 framework_router: &Router<Dictionary>,
856 capability_sourced_capabilities_dict: &Dictionary,
857 error_reporter: impl ErrorReporter,
858 aggregate_router_fn: &AggregateRouterFn<C>,
859) -> Router<DirConnector> {
860 let mut aggregate_sources = vec![];
861 let dict_for_source_router = Dictionary::new();
862 let source = new_aggregate_capability_source(component.moniker().clone(), offer_bundle.clone());
863 for offer in offer_bundle.iter() {
864 if matches!(&source, &CapabilitySource::FilteredAggregateProvider(_)) {
865 if let cm_rust::offer::OfferDecl::Service(offer_service_decl) = offer {
866 if offer_service_decl
867 .source_instance_filter
868 .as_ref()
869 .and_then(|v| v.first())
870 .is_none()
871 && offer_service_decl
872 .renamed_instances
873 .as_ref()
874 .and_then(|v| v.first())
875 .is_none()
876 {
877 continue;
881 }
882 }
883 }
884 extend_dict_with_offer::<DirConnector, _>(
885 component,
886 &child_component_output_dictionary_routers,
887 &component_input,
888 &program_output_dict,
889 framework_router,
890 &capability_sourced_capabilities_dict,
891 offer,
892 &dict_for_source_router,
893 error_reporter.clone(),
894 );
895 match dict_for_source_router.remove(offer.target_name()) {
896 Some(Capability::DirConnectorRouter(router)) => {
897 let source_instance = match offer.source() {
898 cm_rust::offer::OfferSource::Self_ => AggregateInstance::Self_,
899 cm_rust::offer::OfferSource::Parent => AggregateInstance::Parent,
900 cm_rust::offer::OfferSource::Child(child_ref) => {
901 AggregateInstance::Child(moniker::ChildName::new(
902 child_ref.name.clone(),
903 child_ref.collection.clone(),
904 ))
905 }
906 other_source => {
907 warn!("unsupported source found in offer aggregate: {:?}", other_source);
908 continue;
909 }
910 };
911 aggregate_sources.push(AggregateSource::DirectoryRouter { source_instance, router })
912 }
913 None => match offer.source() {
914 cm_rust::offer::OfferSource::Collection(collection_name) => {
918 aggregate_sources.push(AggregateSource::Collection {
919 collection_name: collection_name.clone(),
920 });
921 }
922 _ => continue,
923 },
924 other => warn!("found unexpected entry in dictionary: {:?}", other),
925 }
926 }
927 (aggregate_router_fn)(component.clone(), aggregate_sources, source)
928}
929
930fn new_aggregate_capability_source(
931 moniker: Moniker,
932 offers: Vec<&cm_rust::offer::OfferDecl>,
933) -> CapabilitySource {
934 let offer_service_decls = offers
935 .iter()
936 .map(|o| match o {
937 cm_rust::offer::OfferDecl::Service(o) => o.as_ref(),
938 _ => panic!(
939 "cannot aggregate non-service capabilities, manifest validation should prevent this"
940 ),
941 })
942 .collect::<Vec<_>>();
943 let is_filtered_offer = offer_service_decls.iter().any(|o| {
945 o.source_instance_filter.as_ref().map(|v| !v.is_empty()).unwrap_or(false)
946 || o.renamed_instances.as_ref().map(|v| !v.is_empty()).unwrap_or(false)
947 });
948 let capability =
949 AggregateCapability::Service(offer_service_decls.first().unwrap().target_name.clone());
950 if is_filtered_offer {
951 CapabilitySource::FilteredAggregateProvider(FilteredAggregateProviderSource {
952 capability,
953 moniker,
954 offer_service_decls: offer_service_decls.into_iter().cloned().collect(),
955 })
956 } else {
957 let members = offers.iter().filter_map(|o| AggregateMember::try_from(*o).ok()).collect();
958 CapabilitySource::AnonymizedAggregate(AnonymizedAggregateSource {
959 capability,
960 moniker,
961 members,
962 instances: vec![],
963 })
964 }
965}
966
967fn group_use_aggregates<'a>(
971 uses: &'a [cm_rust::UseDecl],
972) -> impl Iterator<Item = Vec<&'a cm_rust::UseDecl>> + 'a {
973 let mut groupings = HashMap::with_capacity(uses.len());
974 let mut ungroupable_uses = Vec::new();
975 for use_ in uses.iter() {
976 if let Some(target_path) = use_.path() {
977 groupings.entry(target_path).or_insert_with(|| Vec::with_capacity(1)).push(use_);
978 } else {
979 ungroupable_uses.push(use_);
980 }
981 }
982 groupings.into_values().chain(ungroupable_uses.into_iter().map(|u| vec![u]))
983}
984
985fn group_offer_aggregates<'a>(
989 offers: &'a [cm_rust::offer::OfferDecl],
990) -> impl Iterator<Item = Vec<&'a cm_rust::offer::OfferDecl>> + 'a {
991 let mut groupings = HashMap::with_capacity(offers.len());
992
993 for offer in offers {
994 groupings
995 .entry((offer.target(), offer.target_name()))
996 .or_insert_with(|| Vec::with_capacity(1))
997 .push(offer);
998 }
999 groupings.into_values()
1000}
1001
1002fn group_expose_aggregates<'a>(
1004 exposes: &'a [cm_rust::ExposeDecl],
1005) -> impl Iterator<Item = Vec<&'a cm_rust::ExposeDecl>> + 'a {
1006 let mut groupings = HashMap::with_capacity(exposes.len());
1007 for expose in exposes {
1008 groupings
1009 .entry((expose.target(), expose.target_name()))
1010 .or_insert_with(|| Vec::with_capacity(1))
1011 .push(expose);
1012 }
1013 groupings.into_values()
1014}
1015
1016fn build_environment<C: ComponentInstanceInterface + 'static>(
1017 component: &Arc<C>,
1018 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
1019 component_input: &ComponentInput,
1020 environment_decl: &cm_rust::EnvironmentDecl,
1021 program_output_dict: &Dictionary,
1022 error_reporter: &impl ErrorReporter,
1023) -> ComponentEnvironment {
1024 let mut environment = ComponentEnvironment::new();
1025 if environment_decl.extends == fdecl::EnvironmentExtends::Realm {
1026 if let Ok(e) = component_input.environment().shallow_copy() {
1027 environment = e;
1028 } else {
1029 warn!("failed to copy component_input.environment");
1030 }
1031 }
1032 environment.set_name(&environment_decl.name);
1033 if let Some(stop_timeout_ms) = environment_decl.stop_timeout_ms {
1034 environment.set_stop_timeout(stop_timeout_ms as i64);
1035 }
1036 let debug = environment_decl.debug_capabilities.iter().map(|debug_registration| {
1037 let cm_rust::DebugRegistration::Protocol(debug) = debug_registration;
1038 (
1039 &debug.source_name,
1040 debug.target_name.clone(),
1041 &debug.source,
1042 CapabilityTypeName::Protocol,
1043 RouteRequestErrorInfo::from(debug_registration),
1044 )
1045 });
1046 let runners = environment_decl.runners.iter().map(|runner| {
1047 (
1048 &runner.source_name,
1049 runner.target_name.clone(),
1050 &runner.source,
1051 CapabilityTypeName::Runner,
1052 RouteRequestErrorInfo::from(runner),
1053 )
1054 });
1055 let resolvers = environment_decl.resolvers.iter().map(|resolver| {
1056 (
1057 &resolver.resolver,
1058 Name::new(&resolver.scheme).unwrap(),
1059 &resolver.source,
1060 CapabilityTypeName::Resolver,
1061 RouteRequestErrorInfo::from(resolver),
1062 )
1063 });
1064 let moniker = component.moniker();
1065 for (source_name, target_name, source, porcelain_type, route_request) in
1066 debug.chain(runners).chain(resolvers)
1067 {
1068 let source_path =
1069 SeparatedPath { dirname: Default::default(), basename: source_name.clone() };
1070 let router: Router<Connector> = match &source {
1071 cm_rust::RegistrationSource::Parent => {
1072 use_from_parent_router::<Connector>(component_input, source_path, moniker)
1073 }
1074 cm_rust::RegistrationSource::Self_ => program_output_dict
1075 .get_router_or_not_found::<Connector>(
1076 &source_path,
1077 RoutingError::use_from_self_not_found(
1078 moniker,
1079 source_path.iter_segments().join("/"),
1080 ),
1081 ),
1082 cm_rust::RegistrationSource::Child(child_name) => {
1083 let child_name = ChildName::parse(child_name).expect("invalid child name");
1084 let Some(child_component_output) =
1085 child_component_output_dictionary_routers.get(&child_name)
1086 else {
1087 continue;
1088 };
1089 child_component_output.clone().lazy_get(
1090 source_path,
1091 RoutingError::use_from_child_expose_not_found(
1092 &child_name,
1093 moniker,
1094 source_name.clone(),
1095 ),
1096 )
1097 }
1098 };
1099 let router = router
1100 .with_porcelain_no_default(porcelain_type)
1101 .availability(Availability::Required)
1102 .target(component)
1103 .error_info(route_request)
1104 .error_reporter(error_reporter.clone());
1105 let dict_to_insert_to = match porcelain_type {
1106 CapabilityTypeName::Protocol => environment.debug(),
1107 CapabilityTypeName::Runner => environment.runners(),
1108 CapabilityTypeName::Resolver => environment.resolvers(),
1109 c => panic!("unexpected capability type {}", c),
1110 };
1111 match dict_to_insert_to.insert_capability(&target_name, router.into()) {
1112 Ok(()) => (),
1113 Err(_e) => {
1114 }
1118 }
1119 }
1120 environment
1121}
1122
1123pub fn extend_dict_with_offers<C: ComponentInstanceInterface + 'static>(
1125 component: &Arc<C>,
1126 static_offers: &[cm_rust::offer::OfferDecl],
1127 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
1128 component_input: &ComponentInput,
1129 dynamic_offers: &[cm_rust::offer::OfferDecl],
1130 program_output_dict: &Dictionary,
1131 framework_router: &Router<Dictionary>,
1132 capability_sourced_capabilities_dict: &Dictionary,
1133 target_input: &ComponentInput,
1134 error_reporter: impl ErrorReporter,
1135 aggregate_router_fn: &AggregateRouterFn<C>,
1136) {
1137 for offer_bundle in group_offer_aggregates(dynamic_offers).into_iter() {
1138 let first_offer = offer_bundle.first().unwrap();
1139 match first_offer {
1140 cm_rust::offer::OfferDecl::Service(_) => {
1141 let static_offer_bundles = group_offer_aggregates(static_offers);
1142 let maybe_static_offer_bundle = static_offer_bundles.into_iter().find(|bundle| {
1143 bundle.first().unwrap().target_name() == first_offer.target_name()
1144 });
1145 let mut combined_offer_bundle = offer_bundle.clone();
1146 if let Some(mut static_offer_bundle) = maybe_static_offer_bundle {
1147 let _ = target_input.capabilities().remove(first_offer.target_name());
1152 combined_offer_bundle.append(&mut static_offer_bundle);
1153 }
1154 if combined_offer_bundle.len() == 1
1155 && !matches!(first_offer.source(), cm_rust::offer::OfferSource::Collection(_))
1156 {
1157 extend_dict_with_offer::<DirConnector, _>(
1158 component,
1159 &child_component_output_dictionary_routers,
1160 &component_input,
1161 &program_output_dict,
1162 framework_router,
1163 &capability_sourced_capabilities_dict,
1164 first_offer,
1165 &target_input.capabilities(),
1166 error_reporter.clone(),
1167 )
1168 } else {
1169 let aggregate_router = new_aggregate_router_from_service_offers(
1170 &combined_offer_bundle,
1171 component,
1172 &child_component_output_dictionary_routers,
1173 &component_input,
1174 &program_output_dict,
1175 framework_router,
1176 &capability_sourced_capabilities_dict,
1177 error_reporter.clone(),
1178 aggregate_router_fn,
1179 );
1180 target_input
1181 .capabilities()
1182 .insert(first_offer.target_name().clone(), aggregate_router.into())
1183 .expect("failed to insert capability into target dict");
1184 }
1185 }
1186 cm_rust::offer::OfferDecl::Config(_) => extend_dict_with_offer::<Data, _>(
1187 component,
1188 &child_component_output_dictionary_routers,
1189 component_input,
1190 program_output_dict,
1191 framework_router,
1192 capability_sourced_capabilities_dict,
1193 first_offer,
1194 &target_input.capabilities(),
1195 error_reporter.clone(),
1196 ),
1197 cm_rust::offer::OfferDecl::Dictionary(_) => extend_dict_with_offer::<Dictionary, _>(
1198 component,
1199 &child_component_output_dictionary_routers,
1200 component_input,
1201 program_output_dict,
1202 framework_router,
1203 capability_sourced_capabilities_dict,
1204 first_offer,
1205 &target_input.capabilities(),
1206 error_reporter.clone(),
1207 ),
1208 cm_rust::offer::OfferDecl::Directory(_) | cm_rust::offer::OfferDecl::Storage(_) => {
1209 extend_dict_with_offer::<DirConnector, _>(
1210 component,
1211 &child_component_output_dictionary_routers,
1212 component_input,
1213 program_output_dict,
1214 framework_router,
1215 capability_sourced_capabilities_dict,
1216 first_offer,
1217 &target_input.capabilities(),
1218 error_reporter.clone(),
1219 )
1220 }
1221 cm_rust::offer::OfferDecl::Protocol(_)
1222 | cm_rust::offer::OfferDecl::Runner(_)
1223 | cm_rust::offer::OfferDecl::Resolver(_) => extend_dict_with_offer::<Connector, _>(
1224 component,
1225 &child_component_output_dictionary_routers,
1226 component_input,
1227 program_output_dict,
1228 framework_router,
1229 capability_sourced_capabilities_dict,
1230 first_offer,
1231 &target_input.capabilities(),
1232 error_reporter.clone(),
1233 ),
1234 _ => {}
1235 }
1236 }
1237}
1238
1239pub fn is_supported_use(use_: &cm_rust::UseDecl) -> bool {
1240 matches!(
1241 use_,
1242 cm_rust::UseDecl::Config(_)
1243 | cm_rust::UseDecl::Protocol(_)
1244 | cm_rust::UseDecl::Runner(_)
1245 | cm_rust::UseDecl::Service(_)
1246 | cm_rust::UseDecl::Directory(_)
1247 | cm_rust::UseDecl::EventStream(_)
1248 | cm_rust::UseDecl::Dictionary(_)
1249 | cm_rust::UseDecl::Storage(_)
1250 )
1251}
1252
1253fn extend_dict_with_config_use<C: ComponentInstanceInterface + 'static>(
1256 component: &Arc<C>,
1257 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
1258 component_input: &ComponentInput,
1259 program_input: &ProgramInput,
1260 program_output_dict: &Dictionary,
1261 config_use: &cm_rust::UseConfigurationDecl,
1262 error_reporter: impl ErrorReporter,
1263) {
1264 let moniker = component.moniker();
1265 let source_path = config_use.source_path();
1266 let porcelain_type = CapabilityTypeName::Config;
1267 let router: Router<Data> = match config_use.source() {
1268 cm_rust::UseSource::Parent => {
1269 use_from_parent_router::<Data>(component_input, source_path.to_owned(), moniker)
1270 }
1271 cm_rust::UseSource::Self_ => program_output_dict.get_router_or_not_found::<Data>(
1272 &source_path,
1273 RoutingError::use_from_self_not_found(moniker, source_path.iter_segments().join("/")),
1274 ),
1275 cm_rust::UseSource::Child(child_name) => {
1276 let child_name = ChildName::parse(child_name).expect("invalid child name");
1277 let Some(child_component_output) =
1278 child_component_output_dictionary_routers.get(&child_name)
1279 else {
1280 panic!(
1281 "use declaration in manifest for component {} has a source of a nonexistent child {}, this should be prevented by manifest validation",
1282 moniker, child_name
1283 );
1284 };
1285 child_component_output.clone().lazy_get(
1286 source_path.to_owned(),
1287 RoutingError::use_from_child_expose_not_found(
1288 &child_name,
1289 &moniker,
1290 config_use.source_name().clone(),
1291 ),
1292 )
1293 }
1294 cm_rust::UseSource::Environment => return,
1296 cm_rust::UseSource::Debug => return,
1297 cm_rust::UseSource::Capability(_) => return,
1298 cm_rust::UseSource::Framework => return,
1299 cm_rust::UseSource::Collection(_) => return,
1300 };
1301
1302 let availability = *config_use.availability();
1303 match program_input.config().insert_capability(
1304 &config_use.target_name,
1305 router
1306 .with_porcelain_with_default(porcelain_type)
1307 .availability(availability)
1308 .target(component)
1309 .error_info(config_use)
1310 .error_reporter(error_reporter)
1311 .into(),
1312 ) {
1313 Ok(()) => (),
1314 Err(e) => {
1315 warn!("failed to insert {} in program input dict: {e:?}", config_use.target_name)
1316 }
1317 }
1318}
1319
1320fn extend_dict_with_event_stream_uses<C: ComponentInstanceInterface + 'static>(
1321 component: &Arc<C>,
1322 component_input: &ComponentInput,
1323 program_input: &ProgramInput,
1324 uses: Vec<&cm_rust::UseDecl>,
1325 error_reporter: impl ErrorReporter,
1326 event_stream_use_router_fn: &EventStreamUseRouterFn<C>,
1327) {
1328 let use_event_stream_decls = uses.into_iter().map(|u| match u {
1329 cm_rust::UseDecl::EventStream(decl) => decl,
1330 _other_use => panic!("conflicting use types share target path, this should be prevented by manifest validation"),
1331 }).collect::<Vec<_>>();
1332 let moniker = component.moniker();
1333 let porcelain_type = CapabilityTypeName::EventStream;
1334 let target_path = use_event_stream_decls.first().unwrap().target_path.clone();
1335 for use_event_stream_decl in &use_event_stream_decls {
1336 assert_eq!(
1337 &use_event_stream_decl.source,
1338 &cm_rust::UseSource::Parent,
1339 "event streams can only be used from parent, anything else should be caught by \
1340 manifest validation",
1341 );
1342 }
1343 let routers = use_event_stream_decls
1344 .into_iter()
1345 .map(|use_event_stream_decl| {
1346 let mut route_metadata = finternal::EventStreamRouteMetadata::default();
1347 if let Some(scope) = &use_event_stream_decl.scope {
1348 route_metadata.scope_moniker = Some(component.moniker().to_string());
1349 route_metadata.scope = Some(scope.clone().native_into_fidl());
1350 }
1351
1352 let source_path = use_event_stream_decl.source_path().to_owned();
1353 let router =
1354 use_from_parent_router::<Dictionary>(component_input, source_path, &moniker)
1355 .with_porcelain_with_default(porcelain_type)
1356 .availability(use_event_stream_decl.availability)
1357 .event_stream_route_metadata(route_metadata)
1358 .target(component)
1359 .error_info(RouteRequestErrorInfo::from(use_event_stream_decl.as_ref()))
1360 .error_reporter(error_reporter.clone())
1361 .build();
1362 let filter = use_event_stream_decl.filter.clone();
1363 EventStreamSourceRouter { router, filter }
1364 })
1365 .collect::<Vec<_>>();
1366
1367 let router = event_stream_use_router_fn(component, routers);
1368 if let Err(e) = program_input.namespace().insert_capability(&target_path, router.into()) {
1369 warn!("failed to insert {} in program input dict: {e:?}", target_path)
1370 }
1371}
1372
1373fn extend_dict_with_use<T, C: ComponentInstanceInterface + 'static>(
1374 component: &Arc<C>,
1375 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
1376 component_input: &ComponentInput,
1377 program_input: &ProgramInput,
1378 program_output_dict: &Dictionary,
1379 framework_router: &Router<Dictionary>,
1380 capability_sourced_capabilities_dict: &Dictionary,
1381 use_: &cm_rust::UseDecl,
1382 error_reporter: impl ErrorReporter,
1383) where
1384 T: CapabilityBound + Clone,
1385 Router<T>: TryFrom<Capability> + Into<Capability>,
1386{
1387 if !is_supported_use(use_) {
1388 return;
1389 }
1390 let moniker = component.moniker();
1391 if let cm_rust::UseDecl::Config(config) = use_ {
1392 return extend_dict_with_config_use(
1393 component,
1394 child_component_output_dictionary_routers,
1395 component_input,
1396 program_input,
1397 program_output_dict,
1398 config,
1399 error_reporter,
1400 );
1401 };
1402
1403 let source_path = use_.source_path();
1404 let porcelain_type = CapabilityTypeName::from(use_);
1405 let router: Router<T> = match use_.source() {
1406 cm_rust::UseSource::Parent => {
1407 use_from_parent_router::<T>(component_input, source_path.to_owned(), moniker)
1408 }
1409 cm_rust::UseSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1410 &source_path,
1411 RoutingError::use_from_self_not_found(moniker, source_path.iter_segments().join("/")),
1412 ),
1413 cm_rust::UseSource::Child(child_name) => {
1414 let child_name = ChildName::parse(child_name).expect("invalid child name");
1415 let Some(child_component_output) =
1416 child_component_output_dictionary_routers.get(&child_name)
1417 else {
1418 panic!(
1419 "use declaration in manifest for component {} has a source of a nonexistent child {}, this should be prevented by manifest validation",
1420 moniker, child_name
1421 );
1422 };
1423 child_component_output.clone().lazy_get(
1424 source_path.to_owned(),
1425 RoutingError::use_from_child_expose_not_found(
1426 &child_name,
1427 &moniker,
1428 use_.source_name().clone(),
1429 ),
1430 )
1431 }
1432 cm_rust::UseSource::Framework if use_.is_from_dictionary() => {
1433 Router::<T>::new_error(RoutingError::capability_from_framework_not_found(
1434 moniker,
1435 source_path.iter_segments().join("/"),
1436 ))
1437 }
1438 cm_rust::UseSource::Framework => {
1439 query_framework_router_or_not_found(framework_router, &source_path, component)
1440 }
1441 cm_rust::UseSource::Capability(capability_name) => {
1442 let err = RoutingError::capability_from_capability_not_found(
1443 moniker,
1444 capability_name.as_str().to_string(),
1445 );
1446 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME
1447 || source_path.iter_segments().join("/")
1448 == fcomponent::StorageAdminMarker::PROTOCOL_NAME
1449 {
1450 capability_sourced_capabilities_dict.get_router_or_not_found(&capability_name, err)
1451 } else {
1452 Router::<T>::new_error(err)
1453 }
1454 }
1455 cm_rust::UseSource::Debug => {
1456 let cm_rust::UseDecl::Protocol(use_protocol) = use_ else {
1457 panic!(
1458 "non-protocol capability used with a debug source, this should be prevented by manifest validation"
1459 );
1460 };
1461 component_input.environment().debug().get_router_or_not_found::<T>(
1462 &use_protocol.source_name,
1463 RoutingError::use_from_environment_not_found(
1464 moniker,
1465 "protocol",
1466 &use_protocol.source_name,
1467 ),
1468 )
1469 }
1470 cm_rust::UseSource::Environment => {
1471 let cm_rust::UseDecl::Runner(use_runner) = use_ else {
1472 panic!(
1473 "non-runner capability used with an environment source, this should be prevented by manifest validation"
1474 );
1475 };
1476 component_input.environment().runners().get_router_or_not_found::<T>(
1477 &use_runner.source_name,
1478 RoutingError::use_from_environment_not_found(
1479 moniker,
1480 "runner",
1481 &use_runner.source_name,
1482 ),
1483 )
1484 }
1485 cm_rust::UseSource::Collection(_) => {
1486 return;
1488 }
1489 };
1490
1491 let availability = *use_.availability();
1492 let mut router_builder = router
1493 .with_porcelain_with_default(porcelain_type)
1494 .availability(availability)
1495 .target(&component)
1496 .error_info(use_)
1497 .error_reporter(error_reporter);
1498 if let cm_rust::UseDecl::Directory(decl) = use_ {
1499 router_builder = router_builder
1500 .rights(Some(decl.rights.into()))
1501 .subdir(decl.subdir.clone().into())
1502 .inherit_rights(false);
1503 }
1504 if let cm_rust::UseDecl::Service(_) = use_ {
1505 router_builder = router_builder.rights(Some(fio::R_STAR_DIR.into())).inherit_rights(false);
1506 }
1507 if let cm_rust::UseDecl::Storage(_) = use_ {
1508 router_builder = router_builder
1509 .rights(Some(fidl_fuchsia_io::RW_STAR_DIR.into()))
1510 .subdir(cm_types::RelativePath::dot().into())
1511 .inherit_rights(false);
1512 }
1513 let router = router_builder.build();
1514
1515 match use_ {
1516 cm_rust::UseDecl::Protocol(cm_rust::UseProtocolDecl {
1517 numbered_handle: Some(numbered_handle),
1518 ..
1519 }) => {
1520 let numbered_handle = Name::from(*numbered_handle);
1521 if let Err(e) =
1522 program_input.numbered_handles().insert_capability(&numbered_handle, router.into())
1523 {
1524 warn!("failed to insert {} in program input dict: {e:?}", numbered_handle)
1525 }
1526 }
1527 cm_rust::UseDecl::Runner(_) => {
1528 assert!(program_input.runner().is_none(), "component can't use multiple runners");
1529 program_input.set_runner(router.into());
1530 }
1531 _ => {
1532 if let Err(e) =
1533 program_input.namespace().insert_capability(use_.path().unwrap(), router.into())
1534 {
1535 warn!("failed to insert {} in program input dict: {e:?}", use_.path().unwrap())
1536 }
1537 }
1538 }
1539}
1540
1541fn extend_dict_with_dictionary_use<C: ComponentInstanceInterface + 'static>(
1542 component: &Arc<C>,
1543 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
1544 component_input: &ComponentInput,
1545 program_input: &ProgramInput,
1546 program_output_dict: &Dictionary,
1547 framework_router: &Router<Dictionary>,
1548 capability_sourced_capabilities_dict: &Dictionary,
1549 use_bundle: Vec<&cm_rust::UseDecl>,
1550 error_reporter: impl ErrorReporter,
1551) {
1552 let path = use_bundle[0].path().unwrap();
1553 let mut dictionary_routers = vec![];
1554 for use_ in use_bundle.iter() {
1555 let dict_for_used_router = ProgramInput::new(Dictionary::new(), None, Dictionary::new());
1556 extend_dict_with_use::<Dictionary, _>(
1557 component,
1558 child_component_output_dictionary_routers,
1559 component_input,
1560 &dict_for_used_router,
1561 program_output_dict,
1562 framework_router,
1563 capability_sourced_capabilities_dict,
1564 use_,
1565 error_reporter.clone(),
1566 );
1567 let dictionary_router = match dict_for_used_router.namespace().get_capability(path) {
1568 Some(Capability::DictionaryRouter(router)) => router,
1569 other_value => panic!("unexpected dictionary get result: {other_value:?}"),
1570 };
1571 dictionary_routers.push(dictionary_router);
1572 }
1573 let original_dictionary = match program_input.namespace().get_capability(path) {
1574 Some(Capability::Dictionary(dictionary)) => dictionary,
1575 _ => Dictionary::new(),
1576 };
1577 let router = UseDictionaryRouter::new(
1578 path.clone(),
1579 component.moniker().clone(),
1580 original_dictionary,
1581 dictionary_routers,
1582 CapabilitySource::Component(ComponentSource {
1583 capability: ComponentCapability::Use_((*use_bundle.first().unwrap()).clone()),
1584 moniker: component.moniker().clone(),
1585 }),
1586 );
1587 match program_input.namespace().insert_capability(path, router.into()) {
1588 Ok(()) => (),
1589 Err(_e) => {
1590 }
1594 }
1595}
1596
1597fn use_from_parent_router<T>(
1599 component_input: &ComponentInput,
1600 source_path: impl IterablePath + 'static + Debug,
1601 moniker: &Moniker,
1602) -> Router<T>
1603where
1604 T: CapabilityBound + Clone,
1605 Router<T>: TryFrom<Capability>,
1606{
1607 let err = if moniker == &Moniker::root() {
1608 RoutingError::register_from_component_manager_not_found(
1609 source_path.iter_segments().join("/"),
1610 )
1611 } else {
1612 RoutingError::use_from_parent_not_found(moniker, source_path.iter_segments().join("/"))
1613 };
1614 component_input.capabilities().get_router_or_not_found::<T>(&source_path, err)
1615}
1616
1617fn is_supported_offer(offer: &cm_rust::offer::OfferDecl) -> bool {
1618 matches!(
1619 offer,
1620 cm_rust::offer::OfferDecl::Config(_)
1621 | cm_rust::offer::OfferDecl::Protocol(_)
1622 | cm_rust::offer::OfferDecl::Dictionary(_)
1623 | cm_rust::offer::OfferDecl::Directory(_)
1624 | cm_rust::offer::OfferDecl::Runner(_)
1625 | cm_rust::offer::OfferDecl::Resolver(_)
1626 | cm_rust::offer::OfferDecl::Service(_)
1627 | cm_rust::offer::OfferDecl::EventStream(_)
1628 | cm_rust::offer::OfferDecl::Storage(_)
1629 )
1630}
1631
1632fn extend_dict_with_offer<T, C: ComponentInstanceInterface + 'static>(
1633 component: &Arc<C>,
1634 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
1635 component_input: &ComponentInput,
1636 program_output_dict: &Dictionary,
1637 framework_router: &Router<Dictionary>,
1638 capability_sourced_capabilities_dict: &Dictionary,
1639 offer: &cm_rust::offer::OfferDecl,
1640 target_dict: &Dictionary,
1641 error_reporter: impl ErrorReporter,
1642) where
1643 T: CapabilityBound + Clone,
1644 Router<T>: TryFrom<Capability> + Into<Capability> + WithServiceRenamesAndFilter,
1645{
1646 assert!(is_supported_offer(offer), "{offer:?}");
1647
1648 let source_path = offer.source_path();
1649 let target_name = offer.target_name();
1650 let porcelain_type = CapabilityTypeName::from(offer);
1651 let router: Router<T> = match offer.source() {
1652 cm_rust::offer::OfferSource::Parent => {
1653 let err = if component.moniker() == &Moniker::root() {
1654 RoutingError::register_from_component_manager_not_found(
1655 offer.source_name().to_string(),
1656 )
1657 } else {
1658 RoutingError::offer_from_parent_not_found(
1659 &component.moniker(),
1660 source_path.iter_segments().join("/"),
1661 )
1662 };
1663 component_input.capabilities().get_router_or_not_found::<T>(&source_path, err)
1664 }
1665 cm_rust::offer::OfferSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1666 &source_path,
1667 RoutingError::offer_from_self_not_found(
1668 &component.moniker(),
1669 source_path.iter_segments().join("/"),
1670 ),
1671 ),
1672 cm_rust::offer::OfferSource::Child(child_ref) => {
1673 let child_name: ChildName = child_ref.clone().try_into().expect("invalid child ref");
1674 match child_component_output_dictionary_routers.get(&child_name) {
1675 None => Router::<T>::new_error(RoutingError::offer_from_child_instance_not_found(
1676 &child_name,
1677 &component.moniker(),
1678 source_path.iter_segments().join("/"),
1679 )),
1680 Some(child_component_output) => child_component_output.clone().lazy_get(
1681 source_path.to_owned(),
1682 RoutingError::offer_from_child_expose_not_found(
1683 &child_name,
1684 &component.moniker(),
1685 offer.source_name().clone(),
1686 ),
1687 ),
1688 }
1689 }
1690 cm_rust::offer::OfferSource::Framework => {
1691 if offer.is_from_dictionary() {
1692 warn!(
1693 "routing from framework with dictionary path is not supported: {source_path}"
1694 );
1695 return;
1696 }
1697 query_framework_router_or_not_found(framework_router, &source_path, component)
1698 }
1699 cm_rust::offer::OfferSource::Capability(capability_name) => {
1700 let err = RoutingError::capability_from_capability_not_found(
1701 &component.moniker(),
1702 capability_name.as_str().to_string(),
1703 );
1704 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME
1705 || source_path.iter_segments().join("/")
1706 == fcomponent::StorageAdminMarker::PROTOCOL_NAME
1707 {
1708 capability_sourced_capabilities_dict.get_router_or_not_found(&capability_name, err)
1709 } else {
1710 Router::<T>::new_error(err)
1711 }
1712 }
1713 cm_rust::offer::OfferSource::Void => UnavailableRouter::new_from_offer(offer, component),
1714 cm_rust::offer::OfferSource::Collection(_collection_name) => {
1715 return;
1720 }
1721 };
1722
1723 let availability = *offer.availability();
1724 let mut router_builder = router
1725 .with_porcelain_with_default(porcelain_type)
1726 .availability(availability)
1727 .target(component)
1728 .error_info(offer)
1729 .error_reporter(error_reporter);
1730 if let cm_rust::offer::OfferDecl::Directory(decl) = offer {
1731 router_builder = router_builder
1741 .rights(decl.rights.clone().map(Into::into))
1742 .subdir(decl.subdir.clone().into())
1743 .inherit_rights(true);
1744 }
1745 if let cm_rust::offer::OfferDecl::Storage(_) = offer {
1746 router_builder = router_builder
1747 .rights(Some(fio::RW_STAR_DIR.into()))
1748 .inherit_rights(false)
1749 .subdir(cm_types::RelativePath::dot().into());
1750 }
1751 if let cm_rust::offer::OfferDecl::Service(_) = offer {
1752 router_builder = router_builder.rights(Some(fio::R_STAR_DIR.into())).inherit_rights(true);
1753 }
1754 if let cm_rust::offer::OfferDecl::EventStream(offer_event_stream) = offer {
1755 if let Some(scope) = &offer_event_stream.scope {
1756 router_builder =
1757 router_builder.event_stream_route_metadata(finternal::EventStreamRouteMetadata {
1758 scope_moniker: Some(component.moniker().to_string()),
1759 scope: Some(scope.clone().native_into_fidl()),
1760 ..Default::default()
1761 });
1762 }
1763 }
1764
1765 let router = if let cm_rust::OfferDecl::Service(_) = offer {
1766 router_builder.build().with_service_renames_and_filter(offer.clone())
1767 } else {
1768 router_builder.build().into()
1769 };
1770
1771 match target_dict.insert_capability(target_name, router.into()) {
1772 Ok(()) => (),
1773 Err(e) => warn!("failed to insert {target_name} into target dict: {e:?}"),
1774 }
1775}
1776
1777fn query_framework_router_or_not_found<T, C>(
1778 router: &Router<Dictionary>,
1779 path: &BorrowedSeparatedPath<'_>,
1780 component: &Arc<C>,
1781) -> Router<T>
1782where
1783 T: CapabilityBound,
1784 Router<T>: TryFrom<Capability>,
1785 C: ComponentInstanceInterface + 'static,
1786{
1787 let request = Request { metadata: Dictionary::new() };
1788 let dict: Result<Option<Dictionary>, RouterError> = router
1789 .route(Some(request), component.as_weak().into())
1790 .now_or_never()
1791 .expect("failed to now_or_never");
1792 let dict = match dict {
1793 Ok(Some(dict)) => dict,
1794 _ => Dictionary::new(),
1796 };
1797 dict.get_router_or_not_found::<T>(
1800 path,
1801 RoutingError::capability_from_framework_not_found(
1802 &component.moniker(),
1803 path.iter_segments().join("/"),
1804 ),
1805 )
1806}
1807
1808pub fn is_supported_expose(expose: &cm_rust::ExposeDecl) -> bool {
1809 matches!(
1810 expose,
1811 cm_rust::ExposeDecl::Config(_)
1812 | cm_rust::ExposeDecl::Protocol(_)
1813 | cm_rust::ExposeDecl::Dictionary(_)
1814 | cm_rust::ExposeDecl::Directory(_)
1815 | cm_rust::ExposeDecl::Runner(_)
1816 | cm_rust::ExposeDecl::Resolver(_)
1817 | cm_rust::ExposeDecl::Service(_)
1818 )
1819}
1820
1821fn extend_dict_with_expose<T, C: ComponentInstanceInterface + 'static>(
1822 component: &Arc<C>,
1823 child_component_output_dictionary_routers: &HashMap<ChildName, Router<Dictionary>>,
1824 program_output_dict: &Dictionary,
1825 framework_router: &Router<Dictionary>,
1826 capability_sourced_capabilities_dict: &Dictionary,
1827 expose: &cm_rust::ExposeDecl,
1828 target_component_output: &ComponentOutput,
1829 error_reporter: impl ErrorReporter,
1830) where
1831 T: CapabilityBound + Clone,
1832 Router<T>: TryFrom<Capability> + Into<Capability>,
1833{
1834 assert!(is_supported_expose(expose), "{expose:?}");
1835
1836 let target_dict = match expose.target() {
1837 cm_rust::ExposeTarget::Parent => target_component_output.capabilities(),
1838 cm_rust::ExposeTarget::Framework => target_component_output.framework(),
1839 };
1840 let source_path = expose.source_path();
1841 let target_name = expose.target_name();
1842
1843 let porcelain_type = CapabilityTypeName::from(expose);
1844 let router: Router<T> = match expose.source() {
1845 cm_rust::ExposeSource::Self_ => program_output_dict.get_router_or_not_found::<T>(
1846 &source_path,
1847 RoutingError::expose_from_self_not_found(
1848 &component.moniker(),
1849 source_path.iter_segments().join("/"),
1850 ),
1851 ),
1852 cm_rust::ExposeSource::Child(child_name) => {
1853 let child_name = ChildName::parse(child_name).expect("invalid static child name");
1854 if let Some(child_component_output) =
1855 child_component_output_dictionary_routers.get(&child_name)
1856 {
1857 child_component_output.clone().lazy_get(
1858 source_path.to_owned(),
1859 RoutingError::expose_from_child_expose_not_found(
1860 &child_name,
1861 &component.moniker(),
1862 expose.source_name().clone(),
1863 ),
1864 )
1865 } else {
1866 Router::<T>::new_error(RoutingError::expose_from_child_instance_not_found(
1867 &child_name,
1868 &component.moniker(),
1869 expose.source_name().clone(),
1870 ))
1871 }
1872 }
1873 cm_rust::ExposeSource::Framework => {
1874 if expose.is_from_dictionary() {
1875 warn!(
1876 "routing from framework with dictionary path is not supported: {source_path}"
1877 );
1878 return;
1879 }
1880 query_framework_router_or_not_found(framework_router, &source_path, component)
1881 }
1882 cm_rust::ExposeSource::Capability(capability_name) => {
1883 let err = RoutingError::capability_from_capability_not_found(
1884 &component.moniker(),
1885 capability_name.as_str().to_string(),
1886 );
1887 if source_path.iter_segments().join("/") == fsys::StorageAdminMarker::PROTOCOL_NAME
1888 || source_path.iter_segments().join("/")
1889 == fcomponent::StorageAdminMarker::PROTOCOL_NAME
1890 {
1891 capability_sourced_capabilities_dict
1892 .clone()
1893 .get_router_or_not_found::<T>(&capability_name, err)
1894 } else {
1895 Router::<T>::new_error(err)
1896 }
1897 }
1898 cm_rust::ExposeSource::Void => UnavailableRouter::new_from_expose(expose, component),
1899 cm_rust::ExposeSource::Collection(_name) => return,
1904 };
1905 let availability = *expose.availability();
1906 let mut router_builder = router
1907 .with_porcelain_with_default(porcelain_type)
1908 .availability(availability)
1909 .target(component)
1910 .error_info(expose)
1911 .error_reporter(error_reporter);
1912 if let cm_rust::ExposeDecl::Directory(decl) = expose {
1913 router_builder = router_builder
1914 .rights(decl.rights.clone().map(Into::into))
1915 .subdir(decl.subdir.clone().into())
1916 .inherit_rights(true);
1917 };
1918 if let cm_rust::ExposeDecl::Service(_) = expose {
1919 router_builder = router_builder.rights(Some(fio::R_STAR_DIR.into())).inherit_rights(true);
1920 };
1921 match target_dict.insert_capability(target_name, router_builder.build().into()) {
1922 Ok(()) => (),
1923 Err(e) => warn!("failed to insert {target_name} into target_dict: {e:?}"),
1924 }
1925}
1926
1927struct UnavailableRouter<C: ComponentInstanceInterface> {
1928 capability: InternalCapability,
1929 component: WeakComponentInstanceInterface<C>,
1930}
1931
1932impl<C: ComponentInstanceInterface + 'static> UnavailableRouter<C> {
1933 fn new<T: CapabilityBound>(capability: InternalCapability, component: &Arc<C>) -> Router<T> {
1934 Router::<T>::new(Self { capability, component: component.as_weak() })
1935 }
1936
1937 fn new_from_offer<T: CapabilityBound>(offer: &OfferDecl, component: &Arc<C>) -> Router<T> {
1938 let name = offer.source_name().clone();
1939 let capability = match offer {
1940 OfferDecl::Service(_) => InternalCapability::Service(name),
1941 OfferDecl::Protocol(_) => InternalCapability::Protocol(name),
1942 OfferDecl::Directory(_) => InternalCapability::Directory(name),
1943 OfferDecl::Storage(_) => InternalCapability::Storage(name),
1944 OfferDecl::Runner(_) => InternalCapability::Runner(name),
1945 OfferDecl::Resolver(_) => InternalCapability::Resolver(name),
1946 OfferDecl::EventStream(_) => {
1947 InternalCapability::EventStream(InternalEventStreamCapability {
1948 name,
1949 route_metadata: Default::default(),
1950 })
1951 }
1952 OfferDecl::Dictionary(_) => InternalCapability::Dictionary(name),
1953 OfferDecl::Config(_) => InternalCapability::Config(name),
1954 };
1955 Self::new(capability, component)
1956 }
1957
1958 fn new_from_expose<T: CapabilityBound>(expose: &ExposeDecl, component: &Arc<C>) -> Router<T> {
1959 let name = expose.source_name().clone();
1960 let capability = match expose {
1961 ExposeDecl::Service(_) => InternalCapability::Service(name),
1962 ExposeDecl::Protocol(_) => InternalCapability::Protocol(name),
1963 ExposeDecl::Directory(_) => InternalCapability::Directory(name),
1964 ExposeDecl::Runner(_) => InternalCapability::Runner(name),
1965 ExposeDecl::Resolver(_) => InternalCapability::Resolver(name),
1966 ExposeDecl::Dictionary(_) => InternalCapability::Dictionary(name),
1967 ExposeDecl::Config(_) => InternalCapability::Config(name),
1968 };
1969 Self::new(capability, component)
1970 }
1971}
1972
1973#[async_trait]
1974impl<T: CapabilityBound, C: ComponentInstanceInterface + 'static> Routable<T>
1975 for UnavailableRouter<C>
1976{
1977 async fn route(
1978 &self,
1979 request: Option<Request>,
1980 _target: WeakInstanceToken,
1981 ) -> Result<Option<T>, RouterError> {
1982 let request = request.ok_or_else(|| RouterError::InvalidArgs)?;
1983 let availability = request.metadata.get_metadata().ok_or(RouterError::InvalidArgs)?;
1984 match availability {
1985 cm_rust::Availability::Required | cm_rust::Availability::SameAsTarget => {
1986 Err(RoutingError::SourceCapabilityIsVoid {
1987 moniker: self.component.moniker.clone(),
1988 }
1989 .into())
1990 }
1991 cm_rust::Availability::Optional | cm_rust::Availability::Transitional => Ok(None),
1992 }
1993 }
1994
1995 async fn route_debug(
1996 &self,
1997 _request: Option<Request>,
1998 _target: WeakInstanceToken,
1999 ) -> Result<Data, RouterError> {
2000 let data = CapabilitySource::Void(VoidSource {
2001 capability: self.capability.clone(),
2002 moniker: self.component.moniker.clone(),
2003 })
2004 .try_into()
2005 .expect("failed to convert capability source to Data");
2006 return Ok(data);
2007 }
2008}