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