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