blob: aa0520c7718c6ce198f4bd774b0abccdec1c47f7 [file] [log] [blame]
// Copyright 2021 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 {
anyhow::Error,
fidl::endpoints::{DiscoverableProtocolMarker, Proxy, ServiceMarker},
fuchsia_async as fasync, fuchsia_zircon as zx,
std::fmt,
};
enum ExposedCapabilities {
/// v1 component App
App(fuchsia_component::client::App),
/// v2 component exposed capabilities directory
Directory(zx::Channel),
}
impl fmt::Debug for ExposedCapabilities {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ExposedCapabilities::App(_) => write!(f, "CFv1 App"),
ExposedCapabilities::Directory(_) => write!(f, "CFv2 exposed capabilities Directory"),
}
}
}
/// Represents a component launched by an Element Manager.
///
/// The component can be either a v1 component launched by the fuchsia.sys.Launcher, or a v2
/// component launched as a child of the Element Manager's realm.
///
/// The Element can be used to connect to services exposed by the underlying v1 or v2 component.
#[derive(Debug)]
pub struct Element {
/// CF v1 or v2 object that manages a `Directory` request channel for requesting services
/// exposed by the component.
exposed_capabilities: ExposedCapabilities,
/// The component URL used to launch the component.
// TODO(fxbug.dev/84729)
#[allow(unused)]
url: String,
/// v2 component child name, or empty string if not a child of the realm (such as a CFv1
/// component).
// TODO(fxbug.dev/84729)
#[allow(unused)]
name: String,
/// v2 component child collection name or empty string if not a child of the realm (such as a
/// CFv1 component).
// TODO(fxbug.dev/84729)
#[allow(unused)]
collection: String,
}
/// A component launched in response to `ElementManager::ProposeElement()`.
///
/// A session uses `ElementManager` to launch and return the Element, and can then use the Element
/// to connect to exposed capabilities.
///
/// An Element composes either a CFv2 component (launched as a child of the `ElementManager`'s
/// realm) or a CFv1 component (launched via a fuchsia::sys::Launcher).
impl Element {
/// Creates an Element from a `fuchsia_component::client::App`.
///
/// # Parameters
/// - `url`: The launched component URL.
/// - `app`: The v1 component wrapped in an App, returned by the launch function.
pub fn from_app(app: fuchsia_component::client::App, url: &str) -> Element {
Element {
exposed_capabilities: ExposedCapabilities::App(app),
url: url.to_string(),
name: "".to_string(),
collection: "".to_string(),
}
}
/// Creates an Element from a component's exposed capabilities directory.
///
/// # Parameters
/// - `directory_channel`: A channel to the component's `Directory` of exposed capabilities.
/// - `name`: The launched component's name.
/// - `url`: The launched component URL.
/// - `collection`: The launched component's collection name.
pub fn from_directory_channel(
directory_channel: zx::Channel,
name: &str,
url: &str,
collection: &str,
) -> Element {
Element {
exposed_capabilities: ExposedCapabilities::Directory(directory_channel),
url: url.to_string(),
name: name.to_string(),
collection: collection.to_string(),
}
}
// # Note
//
// The methods below are copied from fuchsia_component::client::App in order to offer
// services in the same way, but from any `Element`, wrapping either a v1 `App` or a v2
// component's `Directory` of exposed services.
/// Returns a reference to the component's `Directory` of exposed capabilities. A session can
/// request services, and/or other capabilities, from the Element, using this channel.
///
/// # Returns
/// A `channel` to the component's `Directory` of exposed capabilities.
#[inline]
pub fn directory_channel(&self) -> &zx::Channel {
match &self.exposed_capabilities {
ExposedCapabilities::App(app) => &app.directory_channel(),
ExposedCapabilities::Directory(directory_channel) => &directory_channel,
}
}
/// Connect to a protocol provided by the `Element`.
///
/// # Type Parameters
/// - P: A FIDL service `Marker` type.
///
/// # Returns
/// - A service `Proxy` matching the `Marker`, or an error if the service is not available from
/// the `Element`.
#[inline]
pub fn connect_to_protocol<P: DiscoverableProtocolMarker>(&self) -> Result<P::Proxy, Error> {
let (client_channel, server_channel) = zx::Channel::create()?;
self.connect_to_protocol_with_channel::<P>(server_channel)?;
Ok(P::Proxy::from_channel(fasync::Channel::from_channel(client_channel)?))
}
/// Connect to the "default" instance of a FIDL service provided by the `Element`.
///
/// # Type Parameters
/// - US: A FIDL service `Marker` type.
///
/// # Returns
/// - A service `Proxy` matching the `Marker`, or an error if the service is not available from
/// the `Element`.
#[inline]
#[allow(unused)]
pub fn connect_to_service<US: ServiceMarker>(&self) -> Result<US::Proxy, Error> {
fuchsia_component::client::connect_to_service_at_channel::<US>(&self.directory_channel())
}
/// Connect to a protocol by passing a channel for the server.
///
/// # Type Parameters
/// - P: A FIDL service `Marker` type.
///
/// # Parameters
/// - server_channel: The server-side endpoint of a channel pair, to bind to the requested
/// service. The caller will interact with the service via the client-side endpoint.
///
/// # Returns
/// - Result::Ok or an error if the service is not available from the `Element`.
#[inline]
pub fn connect_to_protocol_with_channel<P: DiscoverableProtocolMarker>(
&self,
server_channel: zx::Channel,
) -> Result<(), Error> {
self.connect_to_named_protocol_with_channel(P::PROTOCOL_NAME, server_channel)
}
/// Connect to a protocol by name.
///
/// # Parameters
/// - service_name: A FIDL service by name.
/// - server_channel: The server-side endpoint of a channel pair, to bind to the requested
/// service. The caller will interact with the service via the client-side endpoint.
///
/// # Returns
/// - Result::Ok or an error if the service is not available from the `Element`.
#[inline]
pub fn connect_to_named_protocol_with_channel(
&self,
protocol_name: &str,
server_channel: zx::Channel,
) -> Result<(), Error> {
fdio::service_connect_at(&self.directory_channel(), protocol_name, server_channel)?;
Ok(())
}
}