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