[session] Add ElementManager example
This adds an example session which integrates a single element proposer
and handles ProposeElement requests.
BUG: 38577
Change-Id: I7a548dc3ef2ad3600b97eee6586206f4beea67ac
diff --git a/src/session/BUILD.gn b/src/session/BUILD.gn
index 02ee52c..6234ec8 100644
--- a/src/session/BUILD.gn
+++ b/src/session/BUILD.gn
@@ -15,7 +15,8 @@
testonly = true
deps = [
- "//src/session/example_sessions/graphical_session",
+ "//src/session/examples/elements",
+ "//src/session/examples/graphical_session",
]
}
diff --git a/src/session/component_manager/meta/component_manager_sfw.cmx b/src/session/component_manager/meta/component_manager_sfw.cmx
index a10f74d..fa0eb05 100644
--- a/src/session/component_manager/meta/component_manager_sfw.cmx
+++ b/src/session/component_manager/meta/component_manager_sfw.cmx
@@ -11,6 +11,7 @@
"fuchsia.pkg.PackageResolver",
"fuchsia.process.Launcher",
"fuchsia.logger.LogSink",
+ "fuchsia.sys.Loader",
"fuchsia.ui.scenic.Scenic"
]
}
diff --git a/src/session/element_management/src/lib.rs b/src/session/element_management/src/lib.rs
index 70ce33b..f22939a 100644
--- a/src/session/element_management/src/lib.rs
+++ b/src/session/element_management/src/lib.rs
@@ -100,7 +100,7 @@
/// tracking which elements are running, de-duplicating elements, etc.).
pub struct SimpleElementManager {
/// The realm which this element manager uses to create components.
- realm: fsys::RealmProxy
+ realm: fsys::RealmProxy,
}
impl SimpleElementManager {
diff --git a/src/session/examples/elements/BUILD.gn b/src/session/examples/elements/BUILD.gn
new file mode 100644
index 0000000..4f82154
--- /dev/null
+++ b/src/session/examples/elements/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2019 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.
+
+group("elements") {
+ deps = [
+ "//src/session/examples/elements/element_proposer",
+ "//src/session/examples/elements/element_session",
+ "//src/session/examples/elements/simple_element",
+ ]
+}
diff --git a/src/session/examples/elements/README.md b/src/session/examples/elements/README.md
new file mode 100644
index 0000000..44dddb3
--- /dev/null
+++ b/src/session/examples/elements/README.md
@@ -0,0 +1,74 @@
+## Overview
+
+This directory contains an example implementation of a session that
+instantiates a single element proposer.
+
+The element proposer connects to the [`ElementManager`]() service offered by the
+session, and uses said service to add a [`simple_element`]() to the session.
+
+The `simple_element` then connects to the [`ElementPing`] service exposed to it
+by the session, and throug it notifies the session that it has been successfully
+instantiated.
+
+## Element Session
+
+The element session is configured to:
+
+ 1. Declare a static child: the [`element_proposer`]().
+ 2. Declare a child collection with a name which matches the one passed to the
+ [`ElementManagement`]() library.
+ 3. Expose the `ElementManager` service.
+ 4. Expose the `ElementPing` service.
+ 5. Offer the `ElementManager` service to the `element_proposer` child.
+ 6. Offer the `ElementPing` service to the child collection mentioned above.
+
+Details of how this is done can be found in the [`element_session.cml`]() file.
+
+Once the session is launched, it exposes the aforementioned services and starts
+handling requests.
+
+## Element Proposer
+
+The element proposer is configured to:
+
+ 1. Use the `ElementManager` service.
+
+Details of how this is done can be found in the [`element_proposer.cml`]() file.
+
+Once the element proposer is started it connects to the `ElementManager` service
+and attempts to add an element (`simple_element`) to the session.
+
+## Simple Element
+
+The simple element is configured to:
+
+ 1. Use the `ElementPing` service.
+
+Details of how this is done can be found in the [`simple_element.cml`]() file.
+
+Once the simple element is started, it will call `ElementPing::Ping()`. The
+session will receive the ping and log a confirmation.
+
+
+## How to Run
+
+To run the example, first edit the session manager cml file to set the element
+session's component url as the argument:
+
+```
+"args": [ "-s", "fuchsia-pkg://fuchsia.com/element_session#meta/element_session.cm" ],
+```
+
+TODO(37028): Update this when sessions can be configured without modifying the
+session manager's CML file.
+
+Then run the following commands:
+
+```$sh
+fx set core.x64 --with //src/session:session_framework --with //src/session:examples
+fx build
+fx shell run fuchsia-pkg://fuchsia.com/component_manager_sfw#meta/component_manager_sfw.cmx fuchsia-pkg://fuchsia.com/session_manager#meta/session_manager.cm
+```
+
+The last command should output a message stating that the element's ping has
+been received.
diff --git a/src/session/examples/elements/element_proposer/BUILD.gn b/src/session/examples/elements/element_proposer/BUILD.gn
new file mode 100644
index 0000000..f045a49
--- /dev/null
+++ b/src/session/examples/elements/element_proposer/BUILD.gn
@@ -0,0 +1,38 @@
+# Copyright 2019 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.
+
+import("//build/package.gni")
+import("//build/rust/rustc_binary.gni")
+
+rustc_binary("element_proposer_bin") {
+ name = "element_proposer"
+ edition = "2018"
+
+ deps = [
+ "//garnet/public/lib/fidl/rust/fidl",
+ "//garnet/public/rust/fuchsia-async",
+ "//garnet/public/rust/fuchsia-component",
+ "//sdk/fidl/fuchsia.session:fuchsia.session-rustc",
+ "//third_party/rust_crates:failure",
+ ]
+}
+
+package("element_proposer") {
+ deps = [
+ ":element_proposer_bin",
+ ]
+
+ meta = [
+ {
+ path = rebase_path("meta/element_proposer.cml")
+ dest = "element_proposer.cm"
+ },
+ ]
+
+ binaries = [
+ {
+ name = "element_proposer"
+ },
+ ]
+}
diff --git a/src/session/examples/elements/element_proposer/meta/element_proposer.cml b/src/session/examples/elements/element_proposer/meta/element_proposer.cml
new file mode 100644
index 0000000..1f65764
--- /dev/null
+++ b/src/session/examples/elements/element_proposer/meta/element_proposer.cml
@@ -0,0 +1,10 @@
+{
+ "program": {
+ "binary": "bin/element_proposer"
+ },
+ "use": [
+ {
+ "legacy_service": "/svc/fuchsia.session.ElementManager"
+ },
+ ]
+}
\ No newline at end of file
diff --git a/src/session/examples/elements/element_proposer/src/main.rs b/src/session/examples/elements/element_proposer/src/main.rs
new file mode 100644
index 0000000..313665f
--- /dev/null
+++ b/src/session/examples/elements/element_proposer/src/main.rs
@@ -0,0 +1,28 @@
+// Copyright 2019 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 {
+ failure::{format_err, Error, ResultExt},
+ fidl_fuchsia_session::{ElementManagerMarker, ElementSpec},
+ fuchsia_async as fasync,
+ fuchsia_component::client::connect_to_service,
+};
+
+#[fasync::run_singlethreaded]
+async fn main() -> Result<(), Error> {
+ let element_manager = connect_to_service::<ElementManagerMarker>()
+ .context("Could not connect to element manager service.")?;
+
+ element_manager
+ .propose_element(ElementSpec {
+ component_url: Some(
+ "fuchsia-pkg://fuchsia.com/simple_element#meta/simple_element.cm".to_string(),
+ ),
+ })
+ .await?
+ .map_err(|_| format_err!("Error sending ProposeElement message"))?;
+
+ Ok(())
+}
diff --git a/src/session/examples/elements/element_session/BUILD.gn b/src/session/examples/elements/element_session/BUILD.gn
new file mode 100644
index 0000000..ac39112
--- /dev/null
+++ b/src/session/examples/elements/element_session/BUILD.gn
@@ -0,0 +1,43 @@
+# Copyright 2019 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.
+
+import("//build/package.gni")
+import("//build/rust/rustc_binary.gni")
+
+rustc_binary("element_session_bin") {
+ name = "element_session"
+ edition = "2018"
+
+ deps = [
+ "//garnet/public/lib/fidl/rust/fidl",
+ "//garnet/public/rust/fuchsia-async",
+ "//garnet/public/rust/fuchsia-component",
+ "//sdk/fidl/fuchsia.session:fuchsia.session-rustc",
+ "//sdk/fidl/fuchsia.sys2:fuchsia.sys2-rustc",
+ "//src/session/element_management",
+ "//src/session/examples/elements/fidl:session_examples-rustc",
+ "//third_party/rust_crates:failure",
+ "//third_party/rust_crates:futures-preview",
+ "//third_party/rust_crates:rand",
+ ]
+}
+
+package("element_session") {
+ deps = [
+ ":element_session_bin",
+ ]
+
+ meta = [
+ {
+ path = rebase_path("meta/element_session.cml")
+ dest = "element_session.cm"
+ },
+ ]
+
+ binaries = [
+ {
+ name = "element_session"
+ },
+ ]
+}
diff --git a/src/session/examples/elements/element_session/meta/element_session.cml b/src/session/examples/elements/element_session/meta/element_session.cml
new file mode 100644
index 0000000..a7cc1a9
--- /dev/null
+++ b/src/session/examples/elements/element_session/meta/element_session.cml
@@ -0,0 +1,46 @@
+{
+ "program": {
+ "binary": "bin/element_session"
+ },
+ "children": [
+ {
+ "name": "element_proposer",
+ "url": "fuchsia-pkg://fuchsia.com/element_proposer#meta/element_proposer.cm",
+ "startup": "eager",
+ }
+ ],
+ "collections": [
+ {
+ "name": "elements",
+ "durability": "transient",
+ },
+ ],
+ "expose": [
+ {
+ "legacy_service": "/svc/fuchsia.session.ElementManager",
+ "from": "self",
+ },
+ {
+ "legacy_service": "/svc/fuchsia.session.examples.ElementPing",
+ "from": "self",
+ },
+ ],
+ "use": [
+ {
+ "legacy_service": "/svc/fuchsia.sys2.Realm",
+ "from": "framework",
+ },
+ ],
+ "offer": [
+ {
+ "legacy_service": "/svc/fuchsia.session.examples.ElementPing",
+ "from": "self",
+ "to": [ "#elements" ],
+ },
+ {
+ "legacy_service": "/svc/fuchsia.session.ElementManager",
+ "from": "self",
+ "to": [ "#element_proposer" ],
+ },
+ ],
+}
\ No newline at end of file
diff --git a/src/session/examples/elements/element_session/src/main.rs b/src/session/examples/elements/element_session/src/main.rs
new file mode 100644
index 0000000..6a2a1db
--- /dev/null
+++ b/src/session/examples/elements/element_session/src/main.rs
@@ -0,0 +1,112 @@
+// Copyright 2019 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 {
+ element_management::{ElementManager, ElementManagerError, SimpleElementManager},
+ failure::{Error, ResultExt},
+ fidl_fuchsia_session::{
+ ElementManagerRequest, ElementManagerRequestStream, ProposeElementError,
+ },
+ fidl_fuchsia_session_examples::{ElementPingRequest, ElementPingRequestStream},
+ fidl_fuchsia_sys2 as fsys, fuchsia_async as fasync,
+ fuchsia_component::{client::connect_to_service, server::ServiceFs},
+ futures::{StreamExt, TryStreamExt},
+ rand::{distributions::Alphanumeric, thread_rng, Rng},
+};
+
+enum ExposedServices {
+ ElementManager(ElementManagerRequestStream),
+ ElementPing(ElementPingRequestStream),
+}
+
+// TODO(38577): Write example tests for the element session.
+
+/// The child collection to add elements to. This must match a collection name declared in
+/// this session's CML file.
+const ELEMENT_COLLECTION_NAME: &str = "elements";
+
+/// The maximum number of concurrent requests.
+const NUM_CONCURRENT_REQUESTS: usize = 5;
+
+/// This session exposes one service which is offered to all elements started in the session and
+/// prints a string when an element sends a request to said service.
+///
+/// It also exposes the [`fidl_fuchsia_session.ElementManager`] service which an element proposer
+/// can connect to in order to add an element to the session.
+#[fasync::run_singlethreaded]
+async fn main() -> Result<(), Error> {
+ let mut fs = ServiceFs::new_local();
+ fs.dir("svc").add_fidl_service(ExposedServices::ElementPing);
+ fs.dir("svc").add_fidl_service(ExposedServices::ElementManager);
+
+ fs.take_and_serve_directory_handle()?;
+
+ fs.for_each_concurrent(NUM_CONCURRENT_REQUESTS, move |service_request: ExposedServices| {
+ async move {
+ match service_request {
+ ExposedServices::ElementPing(request_stream) => {
+ handle_element_ping_requests(request_stream)
+ .await
+ .expect("Failed to run element ping service.");
+ }
+ ExposedServices::ElementManager(request_stream) => {
+ handle_element_manager_requests(request_stream)
+ .await
+ .expect("Failed to run element manager service.");
+ }
+ }
+ }
+ })
+ .await;
+
+ Ok(())
+}
+
+async fn handle_element_ping_requests(mut stream: ElementPingRequestStream) -> Result<(), Error> {
+ while let Some(ElementPingRequest::Ping { control_handle: _ }) =
+ stream.try_next().await.context("Error handling ping request stream")?
+ {
+ println!("Element did ping session.");
+ }
+ Ok(())
+}
+
+async fn handle_element_manager_requests(
+ mut stream: ElementManagerRequestStream,
+) -> Result<(), Error> {
+ let realm =
+ connect_to_service::<fsys::RealmMarker>().context("Could not connect to Realm service.")?;
+ let element_manager = SimpleElementManager::new(realm);
+ while let Some(request) =
+ stream.try_next().await.context("Error handling element manager request stream")?
+ {
+ match request {
+ ElementManagerRequest::ProposeElement { spec, responder } => {
+ let mut child_name: String =
+ thread_rng().sample_iter(&Alphanumeric).take(16).collect();
+ child_name.make_ascii_lowercase();
+
+ let mut result = match element_manager
+ .add_element(spec, &child_name, ELEMENT_COLLECTION_NAME)
+ .await
+ {
+ // Most of the errors which could be encountered when adding an element are
+ // not the result of an error by the FIDL client. This lists all the cases
+ // explicitly, but it's up to each session to decide how to map the errors.
+ Ok(_) => Ok(()),
+ Err(ElementManagerError::UrlMissing { .. }) => {
+ Err(ProposeElementError::NotFound)
+ }
+ Err(ElementManagerError::NotCreated { .. }) => {
+ Err(ProposeElementError::Rejected)
+ }
+ Err(ElementManagerError::NotBound { .. }) => Err(ProposeElementError::Rejected),
+ };
+
+ let _ = responder.send(&mut result);
+ }
+ }
+ }
+ Ok(())
+}
diff --git a/src/session/examples/elements/fidl/BUILD.gn b/src/session/examples/elements/fidl/BUILD.gn
new file mode 100644
index 0000000..1cb9ca3
--- /dev/null
+++ b/src/session/examples/elements/fidl/BUILD.gn
@@ -0,0 +1,13 @@
+# Copyright 2019 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.
+
+import("//build/fidl/fidl.gni")
+
+fidl("session_examples") {
+ name = "fuchsia.session.examples"
+
+ sources = [
+ "element_ping.fidl",
+ ]
+}
diff --git a/src/session/examples/elements/fidl/element_ping.fidl b/src/session/examples/elements/fidl/element_ping.fidl
new file mode 100644
index 0000000..a175eae
--- /dev/null
+++ b/src/session/examples/elements/fidl/element_ping.fidl
@@ -0,0 +1,13 @@
+// Copyright 2019 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.
+
+library fuchsia.session.examples;
+
+/// A protocol which is used by an example element to ping the session it was
+/// added to.
+[Discoverable]
+protocol ElementPing {
+ /// Sends a ping to the example session.
+ Ping();
+};
diff --git a/src/session/examples/elements/simple_element/BUILD.gn b/src/session/examples/elements/simple_element/BUILD.gn
new file mode 100644
index 0000000..36a1bbd
--- /dev/null
+++ b/src/session/examples/elements/simple_element/BUILD.gn
@@ -0,0 +1,38 @@
+# Copyright 2019 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.
+
+import("//build/package.gni")
+import("//build/rust/rustc_binary.gni")
+
+rustc_binary("simple_element_bin") {
+ name = "simple_element"
+ edition = "2018"
+
+ deps = [
+ "//garnet/public/lib/fidl/rust/fidl",
+ "//garnet/public/rust/fuchsia-async",
+ "//garnet/public/rust/fuchsia-component",
+ "//src/session/examples/elements/fidl:session_examples-rustc",
+ "//third_party/rust_crates:failure",
+ ]
+}
+
+package("simple_element") {
+ deps = [
+ ":simple_element_bin",
+ ]
+
+ meta = [
+ {
+ path = rebase_path("meta/simple_element.cml")
+ dest = "simple_element.cm"
+ },
+ ]
+
+ binaries = [
+ {
+ name = "simple_element"
+ },
+ ]
+}
diff --git a/src/session/examples/elements/simple_element/meta/simple_element.cml b/src/session/examples/elements/simple_element/meta/simple_element.cml
new file mode 100644
index 0000000..cd283f6
--- /dev/null
+++ b/src/session/examples/elements/simple_element/meta/simple_element.cml
@@ -0,0 +1,10 @@
+{
+ "program": {
+ "binary": "bin/simple_element"
+ },
+ "use": [
+ {
+ "legacy_service": "/svc/fuchsia.session.examples.ElementPing",
+ },
+ ],
+}
\ No newline at end of file
diff --git a/src/session/examples/elements/simple_element/src/main.rs b/src/session/examples/elements/simple_element/src/main.rs
new file mode 100644
index 0000000..7b24978
--- /dev/null
+++ b/src/session/examples/elements/simple_element/src/main.rs
@@ -0,0 +1,20 @@
+// Copyright 2019 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 failure::Error;
+
+use {
+ failure::ResultExt, fidl_fuchsia_session_examples::ElementPingMarker, fuchsia_async as fasync,
+ fuchsia_component::client::connect_to_service,
+};
+
+#[fasync::run_singlethreaded]
+async fn main() -> Result<(), Error> {
+ let element_ping = connect_to_service::<ElementPingMarker>()
+ .context("Could not connect to ElementPing service.")?;
+
+ element_ping.ping()?;
+
+ Ok(())
+}
diff --git a/src/session/example_sessions/graphical_session/BUILD.gn b/src/session/examples/graphical_session/BUILD.gn
similarity index 94%
rename from src/session/example_sessions/graphical_session/BUILD.gn
rename to src/session/examples/graphical_session/BUILD.gn
index 4ec34ee..b313cc7 100644
--- a/src/session/example_sessions/graphical_session/BUILD.gn
+++ b/src/session/examples/graphical_session/BUILD.gn
@@ -9,13 +9,12 @@
name = "graphical_session"
edition = "2018"
- visibility = [ "//src/session/example_sessions/graphical_session/*" ]
-
deps = [
"//garnet/public/lib/fidl/rust/fidl",
"//garnet/public/rust/fuchsia-async",
"//garnet/public/rust/fuchsia-component",
"//garnet/public/rust/fuchsia-scenic",
+ "//garnet/public/rust/fuchsia-syslog",
"//garnet/public/rust/fuchsia-zircon",
"//sdk/fidl/fuchsia.images:fuchsia.images-rustc",
"//sdk/fidl/fuchsia.ui.gfx:fuchsia.ui.gfx-rustc",
diff --git a/src/session/example_sessions/graphical_session/meta/graphical_session.cml b/src/session/examples/graphical_session/meta/graphical_session.cml
similarity index 100%
rename from src/session/example_sessions/graphical_session/meta/graphical_session.cml
rename to src/session/examples/graphical_session/meta/graphical_session.cml
diff --git a/src/session/example_sessions/graphical_session/resources/whale.png b/src/session/examples/graphical_session/resources/whale.png
similarity index 100%
rename from src/session/example_sessions/graphical_session/resources/whale.png
rename to src/session/examples/graphical_session/resources/whale.png
Binary files differ
diff --git a/src/session/example_sessions/graphical_session/src/app.rs b/src/session/examples/graphical_session/src/app.rs
similarity index 95%
rename from src/session/example_sessions/graphical_session/src/app.rs
rename to src/session/examples/graphical_session/src/app.rs
index 83164ec..a66924e 100644
--- a/src/session/example_sessions/graphical_session/src/app.rs
+++ b/src/session/examples/graphical_session/src/app.rs
@@ -8,7 +8,7 @@
fidl_fuchsia_ui_scenic::{ScenicMarker, ScenicProxy},
fuchsia_async::{self as fasync},
fuchsia_component::client::connect_to_service,
- fuchsia_scenic,
+ fuchsia_scenic, fuchsia_syslog as syslog,
fuchsia_zircon::{ClockId, Duration, Time},
futures::{StreamExt, TryFutureExt},
};
@@ -94,7 +94,7 @@
.lock()
.present(self.context.lock().unwrap().presentation_time.into_nanos() as u64)
.map_ok(|_| ())
- .unwrap_or_else(|e| eprintln!("present error: {:?}", e)),
+ .unwrap_or_else(|error| syslog::fx_log_err!("Present error: {:?}", error)),
);
}
}
diff --git a/src/session/example_sessions/graphical_session/src/graphics_util.rs b/src/session/examples/graphical_session/src/graphics_util.rs
similarity index 100%
rename from src/session/example_sessions/graphical_session/src/graphics_util.rs
rename to src/session/examples/graphical_session/src/graphics_util.rs
diff --git a/src/session/example_sessions/graphical_session/src/main.rs b/src/session/examples/graphical_session/src/main.rs
similarity index 84%
rename from src/session/example_sessions/graphical_session/src/main.rs
rename to src/session/examples/graphical_session/src/main.rs
index dc3f804..18b7030 100644
--- a/src/session/example_sessions/graphical_session/src/main.rs
+++ b/src/session/examples/graphical_session/src/main.rs
@@ -8,6 +8,8 @@
mod graphics_util;
mod view;
+// TODO(38577): Write example tests for the graphical session.
+
#[fuchsia_async::run_singlethreaded]
async fn main() -> Result<(), Error> {
let mut app = app::App::new().await?;
diff --git a/src/session/example_sessions/graphical_session/src/view.rs b/src/session/examples/graphical_session/src/view.rs
similarity index 100%
rename from src/session/example_sessions/graphical_session/src/view.rs
rename to src/session/examples/graphical_session/src/view.rs