[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