1use crate::bedrock::request_metadata::{Metadata, METADATA_KEY_TYPE};
6use crate::component_instance::{ComponentInstanceInterface, WeakExtendedInstanceInterface};
7use crate::error::{ErrorReporter, RouteRequestErrorInfo, RoutingError};
8use async_trait::async_trait;
9use cm_rust::CapabilityTypeName;
10use cm_types::{Availability, Name};
11use fidl_fuchsia_component_sandbox as fsandbox;
12use moniker::ExtendedMoniker;
13use router_error::RouterError;
14use sandbox::{Capability, CapabilityBound, Data, Dict, Request, Routable, Router, RouterResponse};
15use std::collections::HashMap;
16use std::sync::{Arc, LazyLock};
17use strum::IntoEnumIterator;
18
19struct PorcelainRouter<T: CapabilityBound, R, C: ComponentInstanceInterface, const D: bool> {
20 router: Router<T>,
21 porcelain_type: CapabilityTypeName,
22 availability: Availability,
23 target: WeakExtendedInstanceInterface<C>,
24 route_request: RouteRequestErrorInfo,
25 error_reporter: R,
26}
27
28#[async_trait]
29impl<
30 T: CapabilityBound,
31 R: ErrorReporter,
32 C: ComponentInstanceInterface + 'static,
33 const D: bool,
34 > Routable<T> for PorcelainRouter<T, R, C, D>
35{
36 async fn route(
37 &self,
38 request: Option<Request>,
39 debug: bool,
40 ) -> Result<RouterResponse<T>, RouterError> {
41 match self.do_route(request, debug, D).await {
42 Ok(res) => Ok(res),
43 Err(err) => {
44 self.error_reporter
45 .report(&self.route_request, &err, self.target.clone().into())
46 .await;
47 Err(err)
48 }
49 }
50 }
51}
52
53impl<
54 T: CapabilityBound,
55 R: ErrorReporter,
56 C: ComponentInstanceInterface + 'static,
57 const D: bool,
58 > PorcelainRouter<T, R, C, D>
59{
60 #[inline]
61 async fn do_route(
62 &self,
63 request: Option<Request>,
64 debug: bool,
65 supply_default: bool,
66 ) -> Result<RouterResponse<T>, RouterError> {
67 let PorcelainRouter {
68 router,
69 porcelain_type,
70 availability,
71 target,
72 route_request: _,
73 error_reporter: _,
74 } = self;
75 let request = if let Some(request) = request {
76 request
77 } else {
78 if !supply_default {
79 Err(RouterError::InvalidArgs)?;
80 }
81 let metadata = Dict::new();
82 metadata
83 .insert(
84 Name::new(METADATA_KEY_TYPE).unwrap(),
85 Capability::Data(Data::String(porcelain_type.to_string())),
86 )
87 .expect("failed to build default metadata?");
88 metadata.set_metadata(*availability);
89 Request { target: target.clone().into(), metadata }
90 };
91
92 let moniker: ExtendedMoniker = match &self.target {
93 WeakExtendedInstanceInterface::Component(t) => t.moniker.clone().into(),
94 WeakExtendedInstanceInterface::AboveRoot(_) => ExtendedMoniker::ComponentManager,
95 };
96 check_porcelain_type(&moniker, &request, *porcelain_type)?;
97 let updated_availability = check_availability(&moniker, &request, *availability)?;
98
99 request.metadata.set_metadata(updated_availability);
101 router.route(Some(request), debug).await
102 }
103}
104
105fn check_porcelain_type(
106 moniker: &ExtendedMoniker,
107 request: &Request,
108 expected_type: CapabilityTypeName,
109) -> Result<(), RouterError> {
110 let Capability::Data(Data::String(capability_type)) = request
111 .metadata
112 .get(&cm_types::Name::new(METADATA_KEY_TYPE).unwrap())
113 .map_err(|()| RoutingError::BedrockNotCloneable { moniker: moniker.clone() })?
114 .ok_or_else(|| RoutingError::BedrockMissingCapabilityType {
115 moniker: moniker.clone(),
116 type_name: expected_type.to_string(),
117 })?
118 else {
119 return Err(RoutingError::BedrockNotPresentInDictionary {
120 moniker: moniker.clone(),
121 name: String::from("type"),
122 }
123 .into());
124 };
125 if capability_type != expected_type.to_string() {
126 Err(RoutingError::BedrockWrongCapabilityType {
127 moniker: moniker.clone(),
128 actual: capability_type,
129 expected: expected_type.to_string(),
130 })?;
131 }
132 Ok(())
133}
134
135fn check_availability(
136 moniker: &ExtendedMoniker,
137 request: &Request,
138 availability: Availability,
139) -> Result<Availability, RouterError> {
140 let request_availability = request
143 .metadata
144 .get_metadata()
145 .ok_or(fsandbox::RouterError::InvalidArgs)
146 .inspect_err(|e| {
147 log::error!("request {:?} did not have availability metadata: {e:?}", request.target)
148 })?;
149 crate::availability::advance(&moniker, request_availability, availability)
150 .map_err(|e| RoutingError::from(e).into())
151}
152
153pub type DefaultMetadataFn = Arc<dyn Fn(Availability) -> Dict + Send + Sync + 'static>;
154
155pub struct PorcelainBuilder<
159 T: CapabilityBound,
160 R: ErrorReporter,
161 C: ComponentInstanceInterface + 'static,
162 const D: bool,
163> {
164 router: Router<T>,
165 porcelain_type: CapabilityTypeName,
166 availability: Option<Availability>,
167 target: Option<WeakExtendedInstanceInterface<C>>,
168 error_info: Option<RouteRequestErrorInfo>,
169 error_reporter: Option<R>,
170}
171
172impl<
173 T: CapabilityBound,
174 R: ErrorReporter,
175 C: ComponentInstanceInterface + 'static,
176 const D: bool,
177 > PorcelainBuilder<T, R, C, D>
178{
179 fn new(router: Router<T>, porcelain_type: CapabilityTypeName) -> Self {
180 Self {
181 router,
182 porcelain_type,
183 availability: None,
184 target: None,
185 error_info: None,
186 error_reporter: None,
187 }
188 }
189
190 pub fn availability(mut self, a: Availability) -> Self {
193 self.availability = Some(a);
194 self
195 }
196
197 pub fn target(mut self, t: &Arc<C>) -> Self {
201 self.target = Some(WeakExtendedInstanceInterface::Component(t.as_weak()));
202 self
203 }
204
205 pub fn target_above_root(mut self, t: &Arc<C::TopInstance>) -> Self {
208 self.target = Some(WeakExtendedInstanceInterface::AboveRoot(Arc::downgrade(t)));
209 self
210 }
211
212 pub fn error_info<S>(mut self, r: S) -> Self
216 where
217 RouteRequestErrorInfo: From<S>,
218 {
219 self.error_info = Some(RouteRequestErrorInfo::from(r));
220 self
221 }
222
223 pub fn error_reporter(mut self, r: R) -> Self {
226 self.error_reporter = Some(r);
227 self
228 }
229
230 pub fn build(self) -> Router<T> {
232 Router::new(PorcelainRouter::<T, R, C, D> {
233 router: self.router,
234 porcelain_type: self.porcelain_type,
235 availability: self.availability.expect("must set availability"),
236 target: self.target.expect("must set target"),
237 route_request: self.error_info.expect("must set route_request"),
238 error_reporter: self.error_reporter.expect("must set error_reporter"),
239 })
240 }
241}
242
243impl<
244 R: ErrorReporter,
245 T: CapabilityBound,
246 C: ComponentInstanceInterface + 'static,
247 const D: bool,
248 > From<PorcelainBuilder<T, R, C, D>> for Capability
249where
250 Router<T>: Into<Capability>,
251{
252 fn from(b: PorcelainBuilder<T, R, C, D>) -> Self {
253 b.build().into()
254 }
255}
256
257pub trait WithPorcelain<
259 T: CapabilityBound,
260 R: ErrorReporter,
261 C: ComponentInstanceInterface + 'static,
262>
263{
264 fn with_porcelain_with_default(
271 self,
272 type_: CapabilityTypeName,
273 ) -> PorcelainBuilder<T, R, C, true>;
274
275 fn with_porcelain_no_default(
282 self,
283 type_: CapabilityTypeName,
284 ) -> PorcelainBuilder<T, R, C, false>;
285}
286
287impl<T: CapabilityBound, R: ErrorReporter, C: ComponentInstanceInterface + 'static>
288 WithPorcelain<T, R, C> for Router<T>
289{
290 fn with_porcelain_with_default(
291 self,
292 type_: CapabilityTypeName,
293 ) -> PorcelainBuilder<T, R, C, true> {
294 PorcelainBuilder::<T, R, C, true>::new(self, type_)
295 }
296
297 fn with_porcelain_no_default(
298 self,
299 type_: CapabilityTypeName,
300 ) -> PorcelainBuilder<T, R, C, false> {
301 PorcelainBuilder::<T, R, C, false>::new(self, type_)
302 }
303}
304
305pub fn metadata_for_porcelain_type(
306 typename: CapabilityTypeName,
307) -> Arc<dyn Fn(Availability) -> Dict + Send + Sync + 'static> {
308 type MetadataMap =
309 HashMap<CapabilityTypeName, Arc<dyn Fn(Availability) -> Dict + Send + Sync + 'static>>;
310 static CLOSURES: LazyLock<MetadataMap> = LazyLock::new(|| {
311 fn entry_for_typename(
312 typename: CapabilityTypeName,
313 ) -> (CapabilityTypeName, Arc<dyn Fn(Availability) -> Dict + Send + Sync + 'static>)
314 {
315 let v = Arc::new(move |availability: Availability| {
316 let metadata = Dict::new();
317 metadata
318 .insert(
319 Name::new(METADATA_KEY_TYPE).unwrap(),
320 Capability::Data(Data::String(typename.to_string())),
321 )
322 .expect("failed to build default metadata?");
323 metadata.set_metadata(availability);
324 metadata
325 });
326 (typename, v)
327 }
328 CapabilityTypeName::iter().map(entry_for_typename).collect()
329 });
330 CLOSURES.get(&typename).unwrap().clone()
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336 use crate::bedrock::sandbox_construction::ComponentSandbox;
337 use crate::capability_source::{BuiltinCapabilities, NamespaceCapabilities};
338 use crate::component_instance::{ExtendedInstanceInterface, TopInstanceInterface};
339 use crate::error::ComponentInstanceError;
340 use crate::policy::GlobalPolicyChecker;
341 use crate::{environment, ResolvedInstanceInterface};
342 use assert_matches::assert_matches;
343 use cm_rust_testing::UseBuilder;
344 use cm_types::Url;
345 use moniker::{BorrowedChildName, Moniker};
346 use router_error::{DowncastErrorForTest, RouterError};
347 use sandbox::{Data, Dict};
348 use std::sync::{Arc, Mutex};
349
350 #[derive(Debug)]
351 struct FakeComponent {
352 moniker: Moniker,
353 }
354
355 #[derive(Debug)]
356 struct FakeTopInstance {
357 ns: NamespaceCapabilities,
358 builtin: BuiltinCapabilities,
359 }
360
361 impl TopInstanceInterface for FakeTopInstance {
362 fn namespace_capabilities(&self) -> &NamespaceCapabilities {
363 &self.ns
364 }
365 fn builtin_capabilities(&self) -> &BuiltinCapabilities {
366 &self.builtin
367 }
368 }
369
370 #[async_trait]
371 impl ComponentInstanceInterface for FakeComponent {
372 type TopInstance = FakeTopInstance;
373
374 fn child_moniker(&self) -> Option<&BorrowedChildName> {
375 panic!()
376 }
377
378 fn moniker(&self) -> &Moniker {
379 &self.moniker
380 }
381
382 fn url(&self) -> &Url {
383 panic!()
384 }
385
386 fn environment(&self) -> &environment::Environment<Self> {
387 panic!()
388 }
389
390 fn config_parent_overrides(&self) -> Option<&Vec<cm_rust::ConfigOverride>> {
391 panic!()
392 }
393
394 fn policy_checker(&self) -> &GlobalPolicyChecker {
395 panic!()
396 }
397
398 fn component_id_index(&self) -> &component_id_index::Index {
399 panic!()
400 }
401
402 fn try_get_parent(
403 &self,
404 ) -> Result<ExtendedInstanceInterface<Self>, ComponentInstanceError> {
405 panic!()
406 }
407
408 async fn lock_resolved_state<'a>(
409 self: &'a Arc<Self>,
410 ) -> Result<Box<dyn ResolvedInstanceInterface<Component = Self> + 'a>, ComponentInstanceError>
411 {
412 panic!()
413 }
414
415 async fn component_sandbox(
416 self: &Arc<Self>,
417 ) -> Result<ComponentSandbox, ComponentInstanceError> {
418 panic!()
419 }
420 }
421
422 #[derive(Clone)]
423 struct TestErrorReporter {
424 reported: Arc<Mutex<bool>>,
425 }
426
427 impl TestErrorReporter {
428 fn new() -> Self {
429 Self { reported: Arc::new(Mutex::new(false)) }
430 }
431 }
432
433 #[async_trait]
434 impl ErrorReporter for TestErrorReporter {
435 async fn report(
436 &self,
437 _request: &RouteRequestErrorInfo,
438 _err: &RouterError,
439 _route_target: sandbox::WeakInstanceToken,
440 ) {
441 let mut reported = self.reported.lock().unwrap();
442 if *reported {
443 panic!("report() was called twice");
444 }
445 *reported = true;
446 }
447 }
448
449 fn fake_component() -> Arc<FakeComponent> {
450 Arc::new(FakeComponent { moniker: Moniker::root() })
451 }
452
453 fn error_info() -> cm_rust::UseDecl {
454 UseBuilder::protocol().name("name").build()
455 }
456
457 #[fuchsia::test]
458 async fn success() {
459 let source = Data::String("hello".to_string());
460 let base = Router::<Data>::new_ok(source);
461 let component = fake_component();
462 let proxy = base
463 .with_porcelain_with_default(CapabilityTypeName::Protocol)
464 .availability(Availability::Optional)
465 .target(&component)
466 .error_info(&error_info())
467 .error_reporter(TestErrorReporter::new())
468 .build();
469 let metadata = Dict::new();
470 metadata
471 .insert(
472 Name::new(METADATA_KEY_TYPE).unwrap(),
473 Capability::Data(Data::String(CapabilityTypeName::Protocol.to_string())),
474 )
475 .unwrap();
476 metadata.set_metadata(Availability::Optional);
477
478 let capability = proxy
479 .route(Some(Request { target: component.as_weak().into(), metadata }), false)
480 .await
481 .unwrap();
482 let capability = match capability {
483 RouterResponse::<Data>::Capability(d) => d,
484 _ => panic!(),
485 };
486 assert_eq!(capability, Data::String("hello".to_string()));
487 }
488
489 #[fuchsia::test]
490 async fn type_missing() {
491 let reporter = TestErrorReporter::new();
492 let reported = reporter.reported.clone();
493 let source = Data::String("hello".to_string());
494 let base = Router::<Data>::new_ok(source);
495 let component = fake_component();
496 let proxy = base
497 .with_porcelain_with_default(CapabilityTypeName::Protocol)
498 .availability(Availability::Optional)
499 .target(&component)
500 .error_info(&error_info())
501 .error_reporter(reporter)
502 .build();
503 let metadata = Dict::new();
504 metadata.set_metadata(Availability::Optional);
505
506 let error = proxy
507 .route(Some(Request { target: component.as_weak().into(), metadata }), false)
508 .await
509 .unwrap_err();
510 assert_matches!(
511 error,
512 RouterError::NotFound(err)
513 if matches!(
514 err.downcast_for_test::<RoutingError>(),
515 RoutingError::BedrockMissingCapabilityType {
516 moniker,
517 type_name,
518 } if moniker == &Moniker::root().into() && type_name == "protocol"
519 )
520 );
521 assert!(*reported.lock().unwrap());
522 }
523
524 #[fuchsia::test]
525 async fn type_mismatch() {
526 let reporter = TestErrorReporter::new();
527 let reported = reporter.reported.clone();
528 let source = Data::String("hello".to_string());
529 let base = Router::<Data>::new_ok(source);
530 let component = fake_component();
531 let proxy = base
532 .with_porcelain_with_default(CapabilityTypeName::Protocol)
533 .availability(Availability::Optional)
534 .target(&component)
535 .error_info(&error_info())
536 .error_reporter(reporter)
537 .build();
538 let metadata = Dict::new();
539 metadata
540 .insert(
541 Name::new(METADATA_KEY_TYPE).unwrap(),
542 Capability::Data(Data::String(CapabilityTypeName::Service.to_string())),
543 )
544 .unwrap();
545 metadata.set_metadata(Availability::Optional);
546
547 let error = proxy
548 .route(Some(Request { target: component.as_weak().into(), metadata }), false)
549 .await
550 .unwrap_err();
551 assert_matches!(
552 error,
553 RouterError::NotFound(err)
554 if matches!(
555 err.downcast_for_test::<RoutingError>(),
556 RoutingError::BedrockWrongCapabilityType {
557 moniker,
558 expected,
559 actual
560 } if moniker == &Moniker::root().into()
561 && expected == "protocol" && actual == "service"
562 )
563 );
564 assert!(*reported.lock().unwrap());
565 }
566
567 #[fuchsia::test]
568 async fn availability_mismatch() {
569 let reporter = TestErrorReporter::new();
570 let reported = reporter.reported.clone();
571 let source = Data::String("hello".to_string());
572 let base = Router::<Data>::new_ok(source);
573 let component = fake_component();
574 let proxy = base
575 .with_porcelain_with_default(CapabilityTypeName::Protocol)
576 .availability(Availability::Optional)
577 .target(&component)
578 .error_info(&error_info())
579 .error_reporter(reporter)
580 .build();
581 let metadata = Dict::new();
582 metadata
583 .insert(
584 Name::new(METADATA_KEY_TYPE).unwrap(),
585 Capability::Data(Data::String(CapabilityTypeName::Protocol.to_string())),
586 )
587 .unwrap();
588 metadata.set_metadata(Availability::Required);
589
590 let error = proxy
591 .route(Some(Request { target: component.as_weak().into(), metadata }), false)
592 .await
593 .unwrap_err();
594 assert_matches!(
595 error,
596 RouterError::NotFound(err)
597 if matches!(
598 err.downcast_for_test::<RoutingError>(),
599 RoutingError::AvailabilityRoutingError(
600 crate::error::AvailabilityRoutingError::TargetHasStrongerAvailability {
601 moniker
602 }
603 ) if moniker == &Moniker::root().into()
604 )
605 );
606 assert!(*reported.lock().unwrap());
607 }
608}