[biscotti] Implement LinuxComponents.
Expose a component for each mod that:
1) Implements the ComponentController service.
2) Provides access to the view via a ViewProvider service.
For components launched by URI, you can launch these using any method
that can work with Fuchsia component URIs.
Ex, using tiles_ctl:
(fuchsia) $ tiles_ctl start
(fuchsia) $ tiles_ctl add linux://chromium
Ex, using a session shell:
# Currently this will create a new runner for each mod, which in turn
# will boot a new guest (which is probably not what you want).
(fuchsia) $ sessionctl add_mod linux://chromium
For apps launched in the background (ex: via serial console), you can
bind these views to Fuchsia modules using an empty linux URI.
Ex:
(linux) $ /usr/bin/weston-simple-shm&
(fuchsia) $ tiles_ctl add linux://
Test: launched component both with garcon and from a serial shell.
Verified these can be correctly handled by both ermine and tiles.
Change-Id: I71d067ef21d87c301ac0cba301ae3b42206d4988
diff --git a/bin/guest/pkg/biscotti_guest/bin/BUILD.gn b/bin/guest/pkg/biscotti_guest/bin/BUILD.gn
index 789d6e4..c487721 100644
--- a/bin/guest/pkg/biscotti_guest/bin/BUILD.gn
+++ b/bin/guest/pkg/biscotti_guest/bin/BUILD.gn
@@ -40,6 +40,8 @@
sources = [
"guest.cc",
"guest.h",
+ "linux_component.cc",
+ "linux_component.h",
"linux_runner.cc",
"linux_runner.h",
"log_collector.cc",
@@ -48,6 +50,9 @@
public_configs = [ ":biscotti_net_config" ]
public_deps = [
":protos",
+ "//garnet/public/fidl/fuchsia.ui.app",
+ "//garnet/public/fidl/fuchsia.ui.viewsv1",
+ "//garnet/bin/wayland/fidl:fuchsia.wayland",
"//garnet/public/fidl/fuchsia.guest",
"//garnet/public/fidl/fuchsia.sys",
"//garnet/public/lib/component/cpp",
diff --git a/bin/guest/pkg/biscotti_guest/bin/guest.cc b/bin/guest/pkg/biscotti_guest/bin/guest.cc
index 07ffb1b..d3305da 100644
--- a/bin/guest/pkg/biscotti_guest/bin/guest.cc
+++ b/bin/guest/pkg/biscotti_guest/bin/guest.cc
@@ -46,6 +46,7 @@
static constexpr const char* kContainerImageServer =
"https://storage.googleapis.com/cros-containers";
static constexpr const char* kDefaultContainerUser = "machina";
+static constexpr const char* kLinuxUriScheme = "linux://";
// Minfs max file size is currently just under 4GB.
static constexpr off_t kStatefulImageSize = 4000ul * 1024 * 1024;
@@ -126,7 +127,7 @@
fuchsia::guest::EnvironmentControllerPtr env, fxl::CommandLine cl)
: guest_env_(std::move(env)),
cl_(std::move(cl)),
- wayland_dispatcher_(context) {
+ wayland_dispatcher_(context, fit::bind_member(this, &Guest::OnNewView)) {
guest_env_->GetHostVsockEndpoint(socket_endpoint_.NewRequest());
async_ = async_get_default_dispatcher();
Start();
@@ -715,10 +716,31 @@
void Guest::LaunchApplication(AppLaunchRequest app) {
FXL_CHECK(garcon_) << "Called LaunchApplication without a garcon connection";
- std::string desktop_file_id = std::move(app.application.resolved_url);
- desktop_file_id.erase(0, 8);
- FXL_LOG(INFO) << "Launching: " << desktop_file_id;
+ std::string desktop_file_id = app.application.resolved_url;
+ if (desktop_file_id.rfind(kLinuxUriScheme, 0) == std::string::npos) {
+ FXL_LOG(ERROR) << "Invalid URI: " << desktop_file_id;
+ return;
+ }
+ desktop_file_id.erase(0, strlen(kLinuxUriScheme));
+ if (desktop_file_id == "") {
+ // HACK: we use the empty URI to pick up a view that wasn't associated
+ // with an app launch request. For example, if you started a GUI
+ // application from the serial console, a wayland view will have been
+ // created without a fuchsia component to associate with it.
+ //
+ // We'll need to come up with a more proper solution, but this allows us to
+ // at least do some testing of these views for the time being.
+ auto it = background_views_.begin();
+ if (it == background_views_.end()) {
+ FXL_LOG(INFO) << "No background views available";
+ return;
+ }
+ CreateComponent(std::move(app), it->Bind());
+ background_views_.erase(it);
+ return;
+ }
+ FXL_LOG(INFO) << "Launching: " << desktop_file_id;
grpc::ClientContext context;
vm_tools::container::LaunchApplicationRequest request;
vm_tools::container::LaunchApplicationResponse response;
@@ -733,8 +755,35 @@
}
FXL_LOG(INFO) << "Application launched successfully";
- // TODO: Get view out of wayland bridge.
- // TODO: Create component instance.
+ pending_views_.push_back(std::move(app));
+}
+
+void Guest::OnNewView(
+ fidl::InterfaceHandle<fuchsia::ui::app::ViewProvider> view_provider) {
+ // TODO: This currently just pops a component request off the queue to
+ // associate with the new view. This is obviously racy but will work until
+ // we can pipe though a startup id to provide a more accurate correlation.
+ auto it = pending_views_.begin();
+ if (it == pending_views_.end()) {
+ background_views_.push_back(std::move(view_provider));
+ return;
+ }
+ CreateComponent(std::move(*it), std::move(view_provider));
+ pending_views_.erase(it);
+}
+
+void Guest::CreateComponent(
+ AppLaunchRequest request,
+ fidl::InterfaceHandle<fuchsia::ui::app::ViewProvider> view_provider) {
+ auto component = LinuxComponent::Create(
+ fit::bind_member(this, &Guest::OnComponentTerminated),
+ std::move(request.application), std::move(request.startup_info),
+ std::move(request.controller_request), view_provider.Bind());
+ components_.insert({component.get(), std::move(component)});
+}
+
+void Guest::OnComponentTerminated(const LinuxComponent* component) {
+ components_.erase(component);
}
} // namespace biscotti
diff --git a/bin/guest/pkg/biscotti_guest/bin/guest.h b/bin/guest/pkg/biscotti_guest/bin/guest.h
index 6c61546..5c68661 100644
--- a/bin/guest/pkg/biscotti_guest/bin/guest.h
+++ b/bin/guest/pkg/biscotti_guest/bin/guest.h
@@ -16,6 +16,7 @@
#include <lib/fxl/command_line.h>
#include <lib/guest/scenic_wayland_dispatcher.h>
+#include "garnet/bin/guest/pkg/biscotti_guest/bin/linux_component.h"
#include "garnet/bin/guest/pkg/biscotti_guest/bin/log_collector.h"
#include "garnet/bin/guest/pkg/biscotti_guest/third_party/protos/container_guest.grpc.pb.h"
#include "garnet/bin/guest/pkg/biscotti_guest/third_party/protos/container_host.grpc.pb.h"
@@ -124,6 +125,11 @@
std::unique_ptr<typename Service::Stub> NewVsockStub(uint32_t cid,
uint32_t port);
void LaunchApplication(AppLaunchRequest request);
+ void OnNewView(fidl::InterfaceHandle<fuchsia::ui::app::ViewProvider> view);
+ void CreateComponent(
+ AppLaunchRequest request,
+ fidl::InterfaceHandle<fuchsia::ui::app::ViewProvider> view);
+ void OnComponentTerminated(const LinuxComponent* component);
async_dispatcher_t* async_;
std::unique_ptr<grpc::Server> grpc_server_;
@@ -138,7 +144,17 @@
LogCollector log_collector_;
fxl::CommandLine cl_;
guest::ScenicWaylandDispatcher wayland_dispatcher_;
+ // Requests queued up waiting for the guest to fully boot.
std::deque<AppLaunchRequest> pending_requests_;
+ // Requests that have been dispatched to the container, but have not yet been
+ // associated with a wayland ViewProvider.
+ std::deque<AppLaunchRequest> pending_views_;
+ // Views launched in the background (ex: not using garcon). These can be
+ // returned by requesting a null app URI (linux://).
+ std::deque<fidl::InterfaceHandle<fuchsia::ui::app::ViewProvider>>
+ background_views_;
+ std::unordered_map<const LinuxComponent*, std::unique_ptr<LinuxComponent>>
+ components_;
};
} // namespace biscotti
diff --git a/bin/guest/pkg/biscotti_guest/bin/linux_component.cc b/bin/guest/pkg/biscotti_guest/bin/linux_component.cc
new file mode 100644
index 0000000..c1aa116
--- /dev/null
+++ b/bin/guest/pkg/biscotti_guest/bin/linux_component.cc
@@ -0,0 +1,100 @@
+// 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.
+
+#include "garnet/bin/guest/pkg/biscotti_guest/bin/linux_component.h"
+
+#include <lib/async/default.h>
+#include <zircon/status.h>
+
+namespace biscotti {
+
+// static
+std::unique_ptr<LinuxComponent> LinuxComponent::Create(
+ TerminationCallback termination_callback, fuchsia::sys::Package package,
+ fuchsia::sys::StartupInfo startup_info,
+ fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller,
+ fuchsia::ui::app::ViewProviderPtr remote_view_provider) {
+ FXL_DCHECK(remote_view_provider) << "Missing remote_view_provider";
+ return std::unique_ptr<LinuxComponent>(
+ new LinuxComponent(std::move(termination_callback), std::move(package),
+ std::move(startup_info), std::move(controller),
+ std::move(remote_view_provider)));
+}
+
+LinuxComponent::LinuxComponent(
+ TerminationCallback termination_callback, fuchsia::sys::Package package,
+ fuchsia::sys::StartupInfo startup_info,
+ fidl::InterfaceRequest<fuchsia::sys::ComponentController>
+ application_controller_request,
+ fuchsia::ui::app::ViewProviderPtr remote_view_provider)
+ : termination_callback_(std::move(termination_callback)),
+ application_controller_(this),
+ remote_view_provider_(std::move(remote_view_provider)) {
+ application_controller_.set_error_handler(
+ [this](zx_status_t status) { Kill(); });
+
+ auto& launch_info = startup_info.launch_info;
+ if (launch_info.directory_request) {
+ outgoing_.Serve(std::move(launch_info.directory_request));
+ }
+ outgoing_.AddPublicService<fuchsia::ui::app::ViewProvider>(
+ view_bindings_.GetHandler(this));
+ outgoing_.AddPublicService<fuchsia::ui::viewsv1::ViewProvider>(
+ v1_view_bindings_.GetHandler(this));
+
+ // TODO(CF-268): Remove once services are pulled from the public directory.
+ outgoing_.root_dir()->AddEntry(
+ fuchsia::ui::app::ViewProvider::Name_,
+ fbl::AdoptRef(new fs::Service([this](zx::channel channel) {
+ view_bindings_.AddBinding(
+ this, fidl::InterfaceRequest<fuchsia::ui::app::ViewProvider>(
+ std::move(channel)));
+ return ZX_OK;
+ })));
+ outgoing_.root_dir()->AddEntry(
+ fuchsia::ui::viewsv1::ViewProvider::Name_,
+ fbl::AdoptRef(new fs::Service([this](zx::channel channel) {
+ v1_view_bindings_.AddBinding(
+ this, fidl::InterfaceRequest<fuchsia::ui::viewsv1::ViewProvider>(
+ std::move(channel)));
+ return ZX_OK;
+ })));
+}
+
+LinuxComponent::~LinuxComponent() = default;
+
+// |fuchsia::sys::ComponentController|
+void LinuxComponent::Kill() {
+ application_controller_.events().OnTerminated(
+ 0, fuchsia::sys::TerminationReason::EXITED);
+
+ termination_callback_(this);
+ // WARNING: Don't do anything past this point as this instance may have been
+ // collected.
+}
+
+// |fuchsia::sys::ComponentController|
+void LinuxComponent::Detach() {
+ application_controller_.set_error_handler(nullptr);
+}
+
+// |fuchsia::ui::viewsv1::ViewProvider|
+void LinuxComponent::CreateView(
+ fidl::InterfaceRequest<fuchsia::ui::viewsv1token::ViewOwner> view_owner,
+ fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> services) {
+ CreateView(zx::eventpair(view_owner.TakeChannel().release()),
+ std::move(services), nullptr);
+}
+
+// |fuchsia::ui::app::ViewProvider|
+void LinuxComponent::CreateView(
+ zx::eventpair view_token,
+ fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> incoming_services,
+ fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> outgoing_services) {
+ remote_view_provider_->CreateView(std::move(view_token),
+ std::move(incoming_services),
+ std::move(outgoing_services));
+}
+
+} // namespace biscotti
diff --git a/bin/guest/pkg/biscotti_guest/bin/linux_component.h b/bin/guest/pkg/biscotti_guest/bin/linux_component.h
new file mode 100644
index 0000000..24cf1bd
--- /dev/null
+++ b/bin/guest/pkg/biscotti_guest/bin/linux_component.h
@@ -0,0 +1,72 @@
+// 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.
+
+#ifndef GARNET_BIN_GUEST_PKG_BISCOTTI_GUEST_BIN_LINUX_COMPONENT_H_
+#define GARNET_BIN_GUEST_PKG_BISCOTTI_GUEST_BIN_LINUX_COMPONENT_H_
+
+#include <fuchsia/io/cpp/fidl.h>
+#include <fuchsia/sys/cpp/fidl.h>
+#include <fuchsia/ui/app/cpp/fidl.h>
+#include <fuchsia/ui/viewsv1/cpp/fidl.h>
+#include <fuchsia/ui/viewsv1token/cpp/fidl.h>
+#include <lib/component/cpp/outgoing.h>
+#include <lib/component/cpp/startup_context.h>
+#include <lib/fidl/cpp/binding_set.h>
+#include <lib/fidl/cpp/interface_request.h>
+#include <lib/fit/function.h>
+#include <lib/svc/cpp/service_provider_bridge.h>
+#include <zx/eventpair.h>
+
+namespace biscotti {
+
+// Represents a single linux mod with an associated ViewProvider.
+class LinuxComponent : public fuchsia::sys::ComponentController,
+ public fuchsia::ui::viewsv1::ViewProvider,
+ public fuchsia::ui::app::ViewProvider {
+ public:
+ using TerminationCallback = fit::function<void(const LinuxComponent*)>;
+ static std::unique_ptr<LinuxComponent> Create(
+ TerminationCallback termination_callback, fuchsia::sys::Package package,
+ fuchsia::sys::StartupInfo startup_info,
+ fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller,
+ fuchsia::ui::app::ViewProviderPtr remote_view_provider);
+
+ ~LinuxComponent();
+
+ private:
+ TerminationCallback termination_callback_;
+ fidl::Binding<fuchsia::sys::ComponentController> application_controller_;
+ fidl::InterfaceRequest<fuchsia::io::Directory> directory_request_;
+ component::Outgoing outgoing_;
+ std::unique_ptr<component::StartupContext> startup_context_;
+ fidl::BindingSet<fuchsia::ui::app::ViewProvider> view_bindings_;
+ fidl::BindingSet<fuchsia::ui::viewsv1::ViewProvider> v1_view_bindings_;
+ fuchsia::ui::app::ViewProviderPtr remote_view_provider_;
+
+ LinuxComponent(
+ TerminationCallback termination_callback, fuchsia::sys::Package package,
+ fuchsia::sys::StartupInfo startup_info,
+ fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller,
+ fuchsia::ui::app::ViewProviderPtr remote_view_provider);
+
+ // |fuchsia::sys::ComponentController|
+ void Kill() override;
+ void Detach() override;
+
+ // |fuchsia::ui::viewsv1::ViewProvider|
+ void CreateView(
+ fidl::InterfaceRequest<fuchsia::ui::viewsv1token::ViewOwner> view_owner,
+ fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> services) override;
+
+ // |fuchsia::ui::app::ViewProvider|
+ void CreateView(
+ zx::eventpair view_token,
+ fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> incoming_services,
+ fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> outgoing_services)
+ override;
+};
+
+} // namespace biscotti
+
+#endif // GARNET_BIN_GUEST_PKG_BISCOTTI_GUEST_BIN_LINUX_COMPONENT_H_
diff --git a/bin/wayland/fidl/BUILD.gn b/bin/wayland/fidl/BUILD.gn
new file mode 100644
index 0000000..bcbc39f
--- /dev/null
+++ b/bin/wayland/fidl/BUILD.gn
@@ -0,0 +1,15 @@
+# Copyright 2018 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("fuchsia.wayland") {
+ sources = [
+ "view_producer.fidl",
+ ]
+
+ public_deps = [
+ "//garnet/public/fidl/fuchsia.ui.app",
+ ]
+}
diff --git a/bin/wayland/fidl/view_producer.fidl b/bin/wayland/fidl/view_producer.fidl
new file mode 100644
index 0000000..f656c96
--- /dev/null
+++ b/bin/wayland/fidl/view_producer.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.wayland;
+
+using fuchsia.ui.app;
+
+[Discoverable]
+interface ViewProducer {
+ -> OnNewView(fuchsia.ui.app.ViewProvider view_provider);
+};
+
diff --git a/public/fidl/fuchsia.guest/BUILD.gn b/public/fidl/fuchsia.guest/BUILD.gn
index 4f588c4..7ad7679 100644
--- a/public/fidl/fuchsia.guest/BUILD.gn
+++ b/public/fidl/fuchsia.guest/BUILD.gn
@@ -16,6 +16,7 @@
public_deps = [
"//garnet/public/fidl/fuchsia.math",
"//garnet/public/fidl/fuchsia.sys",
+ "//garnet/public/fidl/fuchsia.ui.app",
"//zircon/public/fidl/fuchsia-io",
]
}
diff --git a/public/lib/guest/BUILD.gn b/public/lib/guest/BUILD.gn
index 1771ac3..4c92053 100644
--- a/public/lib/guest/BUILD.gn
+++ b/public/lib/guest/BUILD.gn
@@ -10,8 +10,10 @@
"scenic_wayland_dispatcher.h",
]
public_deps = [
+ "//garnet/bin/wayland/fidl:fuchsia.wayland",
"//garnet/public/fidl/fuchsia.guest",
"//garnet/public/fidl/fuchsia.sys",
+ "//garnet/public/fidl/fuchsia.ui.app",
"//garnet/public/lib/component/cpp",
"//garnet/public/lib/fxl",
]
diff --git a/public/lib/guest/scenic_wayland_dispatcher.cc b/public/lib/guest/scenic_wayland_dispatcher.cc
index cd55bac..df76217 100644
--- a/public/lib/guest/scenic_wayland_dispatcher.cc
+++ b/public/lib/guest/scenic_wayland_dispatcher.cc
@@ -36,6 +36,9 @@
// Connect to the |WaylandDispatcher| FIDL interface and forward the
// channel along.
services.ConnectToService(dispatcher_.NewRequest());
+ services.ConnectToService(view_producer_.NewRequest());
+ view_producer_.events().OnNewView =
+ fit::bind_member(this, &ScenicWaylandDispatcher::OnNewView);
}
return dispatcher_.get();
@@ -51,4 +54,9 @@
}
}
+void ScenicWaylandDispatcher::OnNewView(
+ fidl::InterfaceHandle<fuchsia::ui::app::ViewProvider> view) {
+ listener_(std::move(view));
+}
+
}; // namespace guest
diff --git a/public/lib/guest/scenic_wayland_dispatcher.h b/public/lib/guest/scenic_wayland_dispatcher.h
index 9a96a0f..2f97c7d 100644
--- a/public/lib/guest/scenic_wayland_dispatcher.h
+++ b/public/lib/guest/scenic_wayland_dispatcher.h
@@ -6,6 +6,7 @@
#define LIB_GUEST_SCENIC_WAYLAND_DISPATCHER_H_
#include <fuchsia/guest/cpp/fidl.h>
+#include <fuchsia/wayland/cpp/fidl.h>
#include <lib/component/cpp/startup_context.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/zx/channel.h>
@@ -18,8 +19,12 @@
// This class is not thread-safe.
class ScenicWaylandDispatcher : public fuchsia::guest::WaylandDispatcher {
public:
- ScenicWaylandDispatcher(component::StartupContext* context)
- : context_(context){};
+ using ViewListener = fit::function<void(
+ fidl::InterfaceHandle<fuchsia::ui::app::ViewProvider>)>;
+
+ ScenicWaylandDispatcher(component::StartupContext* context,
+ ViewListener listener = nullptr)
+ : context_(context), listener_(std::move(listener)){};
// |fuchsia::guest::WaylandDispatcher|
void OnNewConnection(zx::channel channel);
@@ -29,14 +34,17 @@
}
private:
+ void OnNewView(fidl::InterfaceHandle<fuchsia::ui::app::ViewProvider> view);
void Reset(zx_status_t status);
fuchsia::guest::WaylandDispatcher* GetOrStartBridge();
- fidl::Binding<fuchsia::guest::WaylandDispatcher> bindings_{this};
component::StartupContext* context_;
+ ViewListener listener_;
+ fidl::Binding<fuchsia::guest::WaylandDispatcher> bindings_{this};
fuchsia::sys::ComponentControllerPtr bridge_;
fuchsia::guest::WaylandDispatcherPtr dispatcher_;
+ fuchsia::wayland::ViewProducerPtr view_producer_;
};
}; // namespace guest