blob: 0fe23f277a059e9c31988d8e1ba4e68bb6959e26 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::{
capability::{CapabilityProvider, FrameworkCapability, InternalCapabilityProvider},
model::component::WeakComponentInstance,
},
::routing::capability_source::InternalCapability,
async_trait::async_trait,
cm_types::Name,
cm_util::TaskGroup,
fidl::{
endpoints::{self, ClientEnd, DiscoverableProtocolMarker, ServerEnd},
epitaph::ChannelEpitaphExt,
},
fidl_fuchsia_component_sandbox as fsandbox,
fuchsia_zircon::{self as zx, AsHandleRef},
futures::prelude::*,
lazy_static::lazy_static,
router_error::Explain,
sandbox::{Dict, Receiver},
std::sync::Arc,
tracing::warn,
};
lazy_static! {
static ref CAPABILITY_NAME: Name = fsandbox::FactoryMarker::PROTOCOL_NAME.parse().unwrap();
}
struct FactoryCapabilityProvider {
host: Arc<FactoryCapabilityHost>,
}
impl FactoryCapabilityProvider {
fn new(host: Arc<FactoryCapabilityHost>) -> Self {
Self { host }
}
}
#[async_trait]
impl InternalCapabilityProvider for FactoryCapabilityProvider {
async fn open_protocol(self: Box<Self>, server_end: zx::Channel) {
let server_end = ServerEnd::<fsandbox::FactoryMarker>::new(server_end);
// We only need to look up the component matching this scope.
// These operations should all work, even if the component is not running.
let serve_result = self.host.serve(server_end.into_stream().unwrap()).await;
if let Err(error) = serve_result {
// TODO: Set an epitaph to indicate this was an unexpected error.
warn!(%error, "serve failed");
}
}
}
pub struct FactoryCapabilityHost {
tasks: TaskGroup,
}
impl FactoryCapabilityHost {
pub fn new() -> Self {
Self { tasks: TaskGroup::new() }
}
pub async fn serve(
&self,
mut stream: fsandbox::FactoryRequestStream,
) -> Result<(), fidl::Error> {
while let Some(request) = stream.try_next().await? {
let method_name = request.method_name();
let result = self.handle_request(request).await;
match result {
// If the error was PEER_CLOSED then we don't need to log it as a client can
// disconnect while we are processing its request.
Err(error) if !error.is_closed() => {
warn!(%method_name, %error, "Couldn't send Factory response");
}
_ => {}
}
}
Ok(())
}
async fn handle_request(&self, request: fsandbox::FactoryRequest) -> Result<(), fidl::Error> {
match request {
fsandbox::FactoryRequest::OpenHandle { capability, server_end, control_handle: _ } => {
match sandbox::Capability::try_from(fsandbox::Capability::Handle(capability)) {
Ok(capability) => match capability {
sandbox::Capability::OneShotHandle(handle) => {
let server_end: ServerEnd<fsandbox::HandleMarker> = server_end.into();
self.tasks
.spawn(serve_handle(handle, server_end.into_stream().unwrap()));
}
_ => unreachable!(),
},
Err(err) => {
warn!("Error converting token to capability: {err:?}");
_ = server_end.close_with_epitaph(err.as_zx_status());
}
}
}
fsandbox::FactoryRequest::OpenConnector {
capability,
server_end,
control_handle: _,
} => match sandbox::Capability::try_from(fsandbox::Capability::Connector(capability)) {
Ok(capability) => match capability {
sandbox::Capability::Connector(connector) => {
let _ = connector.send(sandbox::Message { channel: server_end });
}
_ => unreachable!(),
},
Err(err) => {
warn!("Error converting token to capability: {err:?}");
_ = server_end.close_with_epitaph(err.as_zx_status());
}
},
fsandbox::FactoryRequest::CreateConnector { receiver, responder } => {
let sender = self.create_connector(receiver);
responder.send(sender)?;
}
fsandbox::FactoryRequest::CreateHandle { handle, responder } => {
let handle = self.create_handle(handle);
responder.send(handle)?;
}
fsandbox::FactoryRequest::CreateDictionary { responder } => {
let client_end = self.create_dictionary();
responder.send(client_end)?;
}
fsandbox::FactoryRequest::_UnknownMethod { ordinal, .. } => {
warn!(%ordinal, "fuchsia.component.sandbox/Factory received unknown method");
}
}
Ok(())
}
fn create_handle(&self, handle: zx::Handle) -> fsandbox::HandleCapability {
fsandbox::HandleCapability::from(sandbox::OneShotHandle::new(handle))
}
fn create_connector(
&self,
receiver_client: ClientEnd<fsandbox::ReceiverMarker>,
) -> fsandbox::Connector {
let (receiver, sender) = Receiver::new();
self.tasks.spawn(async move {
receiver.handle_receiver(receiver_client.into_proxy().unwrap()).await;
});
fsandbox::Connector::from(sender)
}
fn create_dictionary(&self) -> ClientEnd<fsandbox::DictionaryMarker> {
let (client_end, server_end) = endpoints::create_endpoints();
let dict = Dict::new();
let client_end_koid = server_end.basic_info().unwrap().related_koid;
dict.serve_and_register(server_end.into_stream().unwrap(), client_end_koid);
client_end
}
}
async fn serve_handle(handle: sandbox::OneShotHandle, mut stream: fsandbox::HandleRequestStream) {
while let Ok(Some(request)) = stream.try_next().await {
match request {
fsandbox::HandleRequest::GetHandle { responder } => {
if let Err(_err) = responder.send(handle.get_handle()) {
return;
}
}
fsandbox::HandleRequest::_UnknownMethod { ordinal, .. } => {
warn!("Received unknown Handle request with ordinal {ordinal}");
}
}
}
}
pub struct FactoryFrameworkCapability {
host: Arc<FactoryCapabilityHost>,
}
impl FactoryFrameworkCapability {
pub fn new(host: Arc<FactoryCapabilityHost>) -> Self {
Self { host }
}
}
impl FrameworkCapability for FactoryFrameworkCapability {
fn matches(&self, capability: &InternalCapability) -> bool {
capability.matches_protocol(&CAPABILITY_NAME)
}
fn new_provider(
&self,
_scope: WeakComponentInstance,
_target: WeakComponentInstance,
) -> Box<dyn CapabilityProvider> {
Box::new(FactoryCapabilityProvider::new(self.host.clone()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use fidl::endpoints::create_endpoints;
use fidl_fuchsia_component_sandbox as fsandbox;
use fuchsia_async as fasync;
use fuchsia_zircon::{self as zx, HandleBased};
use futures::join;
use sandbox::OneShotHandle;
fn factory() -> (fasync::TaskGroup, fsandbox::FactoryProxy) {
let mut tasks = fasync::TaskGroup::new();
let host = FactoryCapabilityHost::new();
let (factory_proxy, stream) =
endpoints::create_proxy_and_stream::<fsandbox::FactoryMarker>().unwrap();
tasks.spawn(async move {
host.serve(stream).await.unwrap();
});
(tasks, factory_proxy)
}
#[fuchsia::test]
async fn create_handle() {
let (tasks, factory_proxy) = factory();
let event = zx::Event::create();
let expected_koid = event.get_koid().unwrap();
let one_shot = factory_proxy.create_handle(event.into()).await.unwrap();
let (handle_client, handle_server) = create_endpoints::<fsandbox::HandleMarker>();
factory_proxy.open_handle(one_shot, handle_server.into()).unwrap();
let handle_proxy = handle_client.into_proxy().unwrap();
let event_back = handle_proxy.get_handle().await.unwrap().unwrap();
assert!(event_back.get_koid().unwrap() == expected_koid);
drop(handle_proxy);
drop(factory_proxy);
tasks.join().await;
}
/// Tests that the OneShotHandle implementation of the GetHandle method
/// returns the handle held by the OneShotHandle.
#[fuchsia::test]
async fn one_shot_serve_get_handle() {
let (tasks, factory_proxy) = factory();
// Create an Event and get its koid.
let event = zx::Event::create();
let expected_koid = event.get_koid().unwrap();
let one_shot = OneShotHandle::from(event.into_handle());
let fidl_one_shot: fsandbox::HandleCapability = one_shot.into();
let (handle_client, handle_server) = create_endpoints::<fsandbox::HandleMarker>();
factory_proxy.open_handle(fidl_one_shot, handle_server).unwrap();
let handle_proxy = handle_client.into_proxy().unwrap();
let client = async move {
let handle = handle_proxy.get_handle().await.unwrap().unwrap();
// The handle should be for same Event that was in the OneShotHandle.
let got_koid = handle.get_koid().unwrap();
assert_eq!(got_koid, expected_koid);
};
drop(factory_proxy);
join!(client, tasks.join());
}
/// Tests that the OneShotHandle implementation of the HandleCapability.GetHandle method
/// returns the Unavailable error if GetHandle is called twice.
#[fuchsia::test]
async fn one_shot_serve_get_handle_unavailable() {
let (tasks, factory_proxy) = factory();
let event = zx::Event::create();
let expected_koid = event.get_koid().unwrap();
let one_shot = OneShotHandle::from(event.into_handle());
let fidl_one_shot: fsandbox::HandleCapability = one_shot.into();
let (handle_client, handle_server) = create_endpoints::<fsandbox::HandleMarker>();
factory_proxy.open_handle(fidl_one_shot, handle_server).unwrap();
let handle_proxy = handle_client.into_proxy().unwrap();
let client = async move {
let handle = handle_proxy.get_handle().await.unwrap().unwrap();
// The handle should be for same Event that was in the OneShotHandle.
let got_koid = handle.get_koid().unwrap();
assert_eq!(got_koid, expected_koid);
};
drop(factory_proxy);
join!(client, tasks.join());
}
#[fuchsia::test]
async fn create_connector() {
let (_tasks, factory_proxy) = factory();
let (receiver_client_end, mut receiver_stream) =
endpoints::create_request_stream::<fsandbox::ReceiverMarker>().unwrap();
let connector = factory_proxy.create_connector(receiver_client_end).await.unwrap();
let (ch1, _ch2) = zx::Channel::create();
let expected_koid = ch1.get_koid().unwrap();
factory_proxy.open_connector(connector, ch1).unwrap();
let request = receiver_stream.try_next().await.unwrap().unwrap();
if let fsandbox::ReceiverRequest::Receive { channel, .. } = request {
assert_eq!(channel.get_koid().unwrap(), expected_koid);
} else {
panic!("unexpected request");
}
}
#[fuchsia::test]
async fn create_dictionary() {
let (_tasks, factory_proxy) = factory();
let dict = factory_proxy.create_dictionary().await.unwrap();
let dict = dict.into_proxy().unwrap();
// The dictionary is empty.
let items = dict.read().await.unwrap();
assert_eq!(items.len(), 0);
}
}