1use crate::bedrock::request_metadata::{InheritRights, IntermediateRights, Metadata};
6use crate::component_instance::{ComponentInstanceInterface, WeakExtendedInstanceInterface};
7use crate::error::{ErrorReporter, RouteRequestErrorInfo, RoutingError};
8use crate::rights::{Rights, RightsWalker};
9use crate::subdir::SubDir;
10use crate::walk_state::WalkStateUnit;
11use async_trait::async_trait;
12use cm_rust::CapabilityTypeName;
13use cm_types::Availability;
14use moniker::ExtendedMoniker;
15use router_error::RouterError;
16use sandbox::{
17 Capability, CapabilityBound, Dict, Request, Routable, Router, RouterResponse, WeakInstanceToken,
18};
19use std::collections::HashMap;
20use std::sync::{Arc, LazyLock};
21use strum::IntoEnumIterator;
22use {fidl_fuchsia_component_internal as finternal, fidl_fuchsia_component_sandbox as fsandbox};
23
24struct PorcelainRouter<T: CapabilityBound, R, C: ComponentInstanceInterface, const D: bool> {
25 router: Router<T>,
26 porcelain_type: CapabilityTypeName,
27 availability: Availability,
28 rights: Option<Rights>,
29 subdir: Option<SubDir>,
30 inherit_rights: Option<bool>,
31 event_stream_route_metadata: Option<finternal::EventStreamRouteMetadata>,
32 target: WeakExtendedInstanceInterface<C>,
33 route_request: RouteRequestErrorInfo,
34 error_reporter: R,
35}
36
37#[async_trait]
38impl<T: CapabilityBound, R: ErrorReporter, C: ComponentInstanceInterface + 'static, const D: bool>
39 Routable<T> for PorcelainRouter<T, R, C, D>
40{
41 async fn route(
42 &self,
43 request: Option<Request>,
44 debug: bool,
45 target: WeakInstanceToken,
46 ) -> Result<RouterResponse<T>, RouterError> {
47 match self.do_route(request, debug, D, target).await {
48 Ok(res) => Ok(res),
49 Err(err) => {
50 self.error_reporter
51 .report(&self.route_request, &err, self.target.clone().into())
52 .await;
53 Err(err)
54 }
55 }
56 }
57}
58
59impl<T: CapabilityBound, R: ErrorReporter, C: ComponentInstanceInterface + 'static, const D: bool>
60 PorcelainRouter<T, R, C, D>
61{
62 #[inline]
63 async fn do_route(
64 &self,
65 request: Option<Request>,
66 debug: bool,
67 supply_default: bool,
68 target: WeakInstanceToken,
69 ) -> Result<RouterResponse<T>, RouterError> {
70 let PorcelainRouter {
71 router,
72 porcelain_type,
73 availability,
74 rights,
75 subdir,
76 inherit_rights,
77 event_stream_route_metadata,
78 target: _,
79 route_request: _,
80 error_reporter: _,
81 } = self;
82 let request = if let Some(request) = request {
83 request
84 } else {
85 if !supply_default {
86 Err(RouterError::InvalidArgs)?;
87 }
88 let metadata = Dict::new();
89 metadata.set_metadata(*porcelain_type);
90 metadata.set_metadata(*availability);
91 if let Some(rights) = rights {
92 metadata.set_metadata(*rights);
93 }
94 if let Some(inherit_rights) = inherit_rights {
95 metadata.set_metadata(InheritRights(*inherit_rights));
96 }
97 if let Some(esrm) = event_stream_route_metadata.as_ref() {
98 metadata.set_metadata(esrm.clone());
99 }
100 Request { metadata }
101 };
102
103 let moniker: ExtendedMoniker = match &self.target {
104 WeakExtendedInstanceInterface::Component(t) => t.moniker.clone().into(),
105 WeakExtendedInstanceInterface::AboveRoot(_) => ExtendedMoniker::ComponentManager,
106 };
107 check_porcelain_type(&moniker, &request, *porcelain_type)?;
108 let updated_availability = check_availability(&moniker, &request, *availability)?;
109
110 check_and_compute_rights(&moniker, &request, &rights)?;
111 if let Some(new_subdir) = check_and_compute_subdir(&moniker, &request, &subdir)? {
112 request.metadata.set_metadata(new_subdir);
113 }
114 if let Some(esrm) = event_stream_route_metadata.as_ref() {
115 let mut route_metadata: finternal::EventStreamRouteMetadata = request
116 .metadata
117 .get_metadata()
118 .expect("missing event stream route request metadata");
119 if route_metadata.scope.is_none() && esrm.scope.is_some() {
122 route_metadata.scope_moniker = Some(esrm.scope_moniker.clone().unwrap());
123 route_metadata.scope = Some(esrm.scope.clone().unwrap());
124 request.metadata.set_metadata(route_metadata);
125 }
126 }
127
128 request.metadata.set_metadata(updated_availability);
130 router.route(Some(request), debug, target).await
131 }
132}
133
134fn check_porcelain_type(
135 moniker: &ExtendedMoniker,
136 request: &Request,
137 expected_type: CapabilityTypeName,
138) -> Result<(), RouterError> {
139 let capability_type: CapabilityTypeName = request.metadata.get_metadata().ok_or_else(|| {
140 RoutingError::BedrockMissingCapabilityType {
141 type_name: expected_type.to_string(),
142 moniker: moniker.clone(),
143 }
144 })?;
145 if capability_type != expected_type {
146 Err(RoutingError::BedrockWrongCapabilityType {
147 moniker: moniker.clone(),
148 actual: capability_type.to_string(),
149 expected: expected_type.to_string(),
150 })?;
151 }
152 Ok(())
153}
154
155fn check_availability(
156 moniker: &ExtendedMoniker,
157 request: &Request,
158 availability: Availability,
159) -> Result<Availability, RouterError> {
160 let request_availability =
163 request.metadata.get_metadata().ok_or(fsandbox::RouterError::InvalidArgs).inspect_err(
164 |e| log::error!("request {:?} did not have availability metadata: {e:?}", request),
165 )?;
166 crate::availability::advance(&moniker, request_availability, availability)
167 .map_err(|e| RoutingError::from(e).into())
168}
169
170fn check_and_compute_rights(
171 moniker: &ExtendedMoniker,
172 request: &Request,
173 rights: &Option<Rights>,
174) -> Result<(), RouterError> {
175 let Some(rights) = rights else {
176 return Ok(());
177 };
178 let InheritRights(inherit) = request.metadata.get_metadata().ok_or(RouterError::InvalidArgs)?;
179 let request_rights: Rights = match request.metadata.get_metadata() {
180 Some(request_rights) => request_rights,
181 None => {
182 if inherit {
183 request.metadata.set_metadata(*rights);
184 *rights
185 } else {
186 Err(RouterError::InvalidArgs)?
187 }
188 }
189 };
190 let intermediate_rights: Option<IntermediateRights> = request.metadata.get_metadata();
191 let request_rights = RightsWalker::new(request_rights, moniker.clone());
192 let router_rights = RightsWalker::new(*rights, moniker.clone());
193 if let Some(IntermediateRights(intermediate_rights)) = intermediate_rights {
196 let intermediate_rights_walker = RightsWalker::new(intermediate_rights, moniker.clone());
197 intermediate_rights_walker
198 .validate_next(&router_rights)
199 .map_err(|e| router_error::RouterError::from(RoutingError::from(e)))?;
200 };
201 request.metadata.set_metadata(IntermediateRights(*rights));
202 request_rights.validate_next(&router_rights).map_err(RoutingError::from)?;
205 Ok(())
206}
207
208fn check_and_compute_subdir(
209 moniker: &ExtendedMoniker,
210 request: &Request,
211 subdir: &Option<SubDir>,
212) -> Result<Option<SubDir>, RouterError> {
213 let Some(mut subdir_from_decl) = subdir.clone() else {
214 return Ok(None);
215 };
216
217 let request_subdir: Option<SubDir> = request.metadata.get_metadata();
218
219 if let Some(request_subdir) = request_subdir {
220 let success = subdir_from_decl.as_mut().extend(request_subdir.clone().into());
221 if !success {
222 return Err(RoutingError::PathTooLong {
223 moniker: moniker.clone(),
224 path: subdir_from_decl.to_string(),
225 keyword: request_subdir.to_string(),
226 }
227 .into());
228 }
229 }
230 Ok(Some(subdir_from_decl))
231}
232
233pub type DefaultMetadataFn = Arc<dyn Fn(Availability) -> Dict + Send + Sync + 'static>;
234
235pub struct PorcelainBuilder<
239 T: CapabilityBound,
240 R: ErrorReporter,
241 C: ComponentInstanceInterface + 'static,
242 const D: bool,
243> {
244 router: Router<T>,
245 porcelain_type: CapabilityTypeName,
246 availability: Option<Availability>,
247 rights: Option<Rights>,
248 subdir: Option<SubDir>,
249 inherit_rights: Option<bool>,
250 event_stream_route_metadata: Option<finternal::EventStreamRouteMetadata>,
251 target: Option<WeakExtendedInstanceInterface<C>>,
252 error_info: Option<RouteRequestErrorInfo>,
253 error_reporter: Option<R>,
254}
255
256impl<T: CapabilityBound, R: ErrorReporter, C: ComponentInstanceInterface + 'static, const D: bool>
257 PorcelainBuilder<T, R, C, D>
258{
259 fn new(router: Router<T>, porcelain_type: CapabilityTypeName) -> Self {
260 Self {
261 router,
262 porcelain_type,
263 availability: None,
264 rights: None,
265 subdir: None,
266 inherit_rights: None,
267 event_stream_route_metadata: None,
268 target: None,
269 error_info: None,
270 error_reporter: None,
271 }
272 }
273
274 pub fn availability(mut self, a: Availability) -> Self {
277 self.availability = Some(a);
278 self
279 }
280
281 pub fn rights(mut self, rights: Option<Rights>) -> Self {
282 self.rights = rights;
283 self
284 }
285
286 pub fn subdir(mut self, subdir: SubDir) -> Self {
287 self.subdir = Some(subdir);
288 self
289 }
290
291 pub fn inherit_rights(mut self, inherit_rights: bool) -> Self {
292 self.inherit_rights = Some(inherit_rights);
293 self
294 }
295
296 pub fn event_stream_route_metadata(
297 mut self,
298 esrm: finternal::EventStreamRouteMetadata,
299 ) -> Self {
300 self.event_stream_route_metadata = Some(esrm);
301 self
302 }
303
304 pub fn target(mut self, t: &Arc<C>) -> Self {
308 self.target = Some(WeakExtendedInstanceInterface::Component(t.as_weak()));
309 self
310 }
311
312 pub fn target_above_root(mut self, t: &Arc<C::TopInstance>) -> Self {
315 self.target = Some(WeakExtendedInstanceInterface::AboveRoot(Arc::downgrade(t)));
316 self
317 }
318
319 pub fn error_info<S>(mut self, r: S) -> Self
323 where
324 RouteRequestErrorInfo: From<S>,
325 {
326 self.error_info = Some(RouteRequestErrorInfo::from(r));
327 self
328 }
329
330 pub fn error_reporter(mut self, r: R) -> Self {
333 self.error_reporter = Some(r);
334 self
335 }
336
337 pub fn build(self) -> Router<T> {
339 Router::new(PorcelainRouter::<T, R, C, D> {
340 router: self.router,
341 porcelain_type: self.porcelain_type,
342 availability: self.availability.expect("must set availability"),
343 rights: self.rights,
344 subdir: self.subdir,
345 inherit_rights: self.inherit_rights,
346 event_stream_route_metadata: self.event_stream_route_metadata,
347 target: self.target.expect("must set target"),
348 route_request: self.error_info.expect("must set route_request"),
349 error_reporter: self.error_reporter.expect("must set error_reporter"),
350 })
351 }
352}
353
354impl<R: ErrorReporter, T: CapabilityBound, C: ComponentInstanceInterface + 'static, const D: bool>
355 From<PorcelainBuilder<T, R, C, D>> for Capability
356where
357 Router<T>: Into<Capability>,
358{
359 fn from(b: PorcelainBuilder<T, R, C, D>) -> Self {
360 b.build().into()
361 }
362}
363
364pub trait WithPorcelain<
366 T: CapabilityBound,
367 R: ErrorReporter,
368 C: ComponentInstanceInterface + 'static,
369>
370{
371 fn with_porcelain_with_default(
378 self,
379 type_: CapabilityTypeName,
380 ) -> PorcelainBuilder<T, R, C, true>;
381
382 fn with_porcelain_no_default(
389 self,
390 type_: CapabilityTypeName,
391 ) -> PorcelainBuilder<T, R, C, false>;
392}
393
394impl<T: CapabilityBound, R: ErrorReporter, C: ComponentInstanceInterface + 'static>
395 WithPorcelain<T, R, C> for Router<T>
396{
397 fn with_porcelain_with_default(
398 self,
399 type_: CapabilityTypeName,
400 ) -> PorcelainBuilder<T, R, C, true> {
401 PorcelainBuilder::<T, R, C, true>::new(self, type_)
402 }
403
404 fn with_porcelain_no_default(
405 self,
406 type_: CapabilityTypeName,
407 ) -> PorcelainBuilder<T, R, C, false> {
408 PorcelainBuilder::<T, R, C, false>::new(self, type_)
409 }
410}
411
412pub fn metadata_for_porcelain_type(
413 typename: CapabilityTypeName,
414) -> Arc<dyn Fn(Availability) -> Dict + Send + Sync + 'static> {
415 type MetadataMap =
416 HashMap<CapabilityTypeName, Arc<dyn Fn(Availability) -> Dict + Send + Sync + 'static>>;
417 static CLOSURES: LazyLock<MetadataMap> = LazyLock::new(|| {
418 fn entry_for_typename(
419 typename: CapabilityTypeName,
420 ) -> (CapabilityTypeName, Arc<dyn Fn(Availability) -> Dict + Send + Sync + 'static>)
421 {
422 let v = Arc::new(move |availability: Availability| {
423 let metadata = Dict::new();
424 metadata.set_metadata(typename);
425 metadata.set_metadata(availability);
426 metadata
427 });
428 (typename, v)
429 }
430 CapabilityTypeName::iter().map(entry_for_typename).collect()
431 });
432 CLOSURES.get(&typename).unwrap().clone()
433}
434
435#[cfg(test)]
436mod tests {
437 use super::*;
438 use crate::ResolvedInstanceInterface;
439 use crate::bedrock::sandbox_construction::ComponentSandbox;
440 use crate::capability_source::{BuiltinCapabilities, NamespaceCapabilities};
441 use crate::component_instance::{ExtendedInstanceInterface, TopInstanceInterface};
442 use crate::error::ComponentInstanceError;
443 use crate::policy::GlobalPolicyChecker;
444 use assert_matches::assert_matches;
445 use cm_rust_testing::UseBuilder;
446 use cm_types::Url;
447 use fuchsia_sync::Mutex;
448 use moniker::Moniker;
449 use router_error::{DowncastErrorForTest, RouterError};
450 use sandbox::{Data, Dict};
451 use std::sync::Arc;
452
453 #[derive(Debug)]
454 struct FakeComponent {
455 moniker: Moniker,
456 }
457
458 #[derive(Debug)]
459 struct FakeTopInstance {
460 ns: NamespaceCapabilities,
461 builtin: BuiltinCapabilities,
462 }
463
464 impl TopInstanceInterface for FakeTopInstance {
465 fn namespace_capabilities(&self) -> &NamespaceCapabilities {
466 &self.ns
467 }
468 fn builtin_capabilities(&self) -> &BuiltinCapabilities {
469 &self.builtin
470 }
471 }
472
473 #[async_trait]
474 impl ComponentInstanceInterface for FakeComponent {
475 type TopInstance = FakeTopInstance;
476
477 fn moniker(&self) -> &Moniker {
478 &self.moniker
479 }
480
481 fn url(&self) -> &Url {
482 panic!()
483 }
484
485 fn config_parent_overrides(&self) -> Option<&[cm_rust::ConfigOverride]> {
486 panic!()
487 }
488
489 fn policy_checker(&self) -> &GlobalPolicyChecker {
490 panic!()
491 }
492
493 fn component_id_index(&self) -> &component_id_index::Index {
494 panic!()
495 }
496
497 fn try_get_parent(
498 &self,
499 ) -> Result<ExtendedInstanceInterface<Self>, ComponentInstanceError> {
500 panic!()
501 }
502
503 async fn lock_resolved_state<'a>(
504 self: &'a Arc<Self>,
505 ) -> Result<Box<dyn ResolvedInstanceInterface<Component = Self> + 'a>, ComponentInstanceError>
506 {
507 panic!()
508 }
509
510 async fn component_sandbox(
511 self: &Arc<Self>,
512 ) -> Result<ComponentSandbox, ComponentInstanceError> {
513 panic!()
514 }
515 }
516
517 #[derive(Clone)]
518 struct TestErrorReporter {
519 reported: Arc<Mutex<bool>>,
520 }
521
522 impl TestErrorReporter {
523 fn new() -> Self {
524 Self { reported: Arc::new(Mutex::new(false)) }
525 }
526 }
527
528 #[async_trait]
529 impl ErrorReporter for TestErrorReporter {
530 async fn report(
531 &self,
532 _request: &RouteRequestErrorInfo,
533 _err: &RouterError,
534 _route_target: sandbox::WeakInstanceToken,
535 ) {
536 let mut reported = self.reported.lock();
537 if *reported {
538 panic!("report() was called twice");
539 }
540 *reported = true;
541 }
542 }
543
544 fn fake_component() -> Arc<FakeComponent> {
545 Arc::new(FakeComponent { moniker: Moniker::root() })
546 }
547
548 fn error_info() -> cm_rust::UseDecl {
549 UseBuilder::protocol().name("name").build()
550 }
551
552 #[fuchsia::test]
553 async fn success() {
554 let source = Data::String("hello".into());
555 let base = Router::<Data>::new_ok(source);
556 let component = fake_component();
557 let proxy = base
558 .with_porcelain_with_default(CapabilityTypeName::Protocol)
559 .availability(Availability::Optional)
560 .target(&component)
561 .error_info(&error_info())
562 .error_reporter(TestErrorReporter::new())
563 .build();
564 let metadata = Dict::new();
565 metadata.set_metadata(CapabilityTypeName::Protocol);
566 metadata.set_metadata(Availability::Optional);
567
568 let capability = proxy
569 .route(Some(Request { metadata }), false, component.as_weak().into())
570 .await
571 .unwrap();
572 let capability = match capability {
573 RouterResponse::<Data>::Capability(d) => d,
574 _ => panic!(),
575 };
576 assert_eq!(capability, Data::String("hello".into()));
577 }
578
579 #[fuchsia::test]
580 async fn type_missing() {
581 let reporter = TestErrorReporter::new();
582 let reported = reporter.reported.clone();
583 let source = Data::String("hello".into());
584 let base = Router::<Data>::new_ok(source);
585 let component = fake_component();
586 let proxy = base
587 .with_porcelain_with_default(CapabilityTypeName::Protocol)
588 .availability(Availability::Optional)
589 .target(&component)
590 .error_info(&error_info())
591 .error_reporter(reporter)
592 .build();
593 let metadata = Dict::new();
594 metadata.set_metadata(Availability::Optional);
595
596 let error = proxy
597 .route(Some(Request { metadata }), false, component.as_weak().into())
598 .await
599 .unwrap_err();
600 assert_matches!(
601 error,
602 RouterError::NotFound(err)
603 if matches!(
604 err.downcast_for_test::<RoutingError>(),
605 RoutingError::BedrockMissingCapabilityType {
606 moniker,
607 type_name,
608 } if moniker == &Moniker::root().into() && type_name == "protocol"
609 )
610 );
611 assert!(*reported.lock());
612 }
613
614 #[fuchsia::test]
615 async fn type_mismatch() {
616 let reporter = TestErrorReporter::new();
617 let reported = reporter.reported.clone();
618 let source = Data::String("hello".into());
619 let base = Router::<Data>::new_ok(source);
620 let component = fake_component();
621 let proxy = base
622 .with_porcelain_with_default(CapabilityTypeName::Protocol)
623 .availability(Availability::Optional)
624 .target(&component)
625 .error_info(&error_info())
626 .error_reporter(reporter)
627 .build();
628 let metadata = Dict::new();
629 metadata.set_metadata(CapabilityTypeName::Service);
630 metadata.set_metadata(Availability::Optional);
631
632 let error = proxy
633 .route(Some(Request { metadata }), false, component.as_weak().into())
634 .await
635 .unwrap_err();
636 assert_matches!(
637 error,
638 RouterError::NotFound(err)
639 if matches!(
640 err.downcast_for_test::<RoutingError>(),
641 RoutingError::BedrockWrongCapabilityType {
642 moniker,
643 expected,
644 actual
645 } if moniker == &Moniker::root().into()
646 && expected == "protocol" && actual == "service"
647 )
648 );
649 assert!(*reported.lock());
650 }
651
652 #[fuchsia::test]
653 async fn availability_mismatch() {
654 let reporter = TestErrorReporter::new();
655 let reported = reporter.reported.clone();
656 let source = Data::String("hello".into());
657 let base = Router::<Data>::new_ok(source);
658 let component = fake_component();
659 let proxy = base
660 .with_porcelain_with_default(CapabilityTypeName::Protocol)
661 .availability(Availability::Optional)
662 .target(&component)
663 .error_info(&error_info())
664 .error_reporter(reporter)
665 .build();
666 let metadata = Dict::new();
667 metadata.set_metadata(CapabilityTypeName::Protocol);
668 metadata.set_metadata(Availability::Required);
669
670 let error = proxy
671 .route(Some(Request { metadata }), false, component.as_weak().into())
672 .await
673 .unwrap_err();
674 assert_matches!(
675 error,
676 RouterError::NotFound(err)
677 if matches!(
678 err.downcast_for_test::<RoutingError>(),
679 RoutingError::AvailabilityRoutingError(
680 crate::error::AvailabilityRoutingError::TargetHasStrongerAvailability {
681 moniker
682 }
683 ) if moniker == &Moniker::root().into()
684 )
685 );
686 assert!(*reported.lock());
687 }
688}