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