[sys] Service termination callback for testing

- Adds an option for clients to observe launched services' exit code and
termination reason on EnclosingEnvironments.

Change-Id: I5f88c75075d24c6063e089e0ec03ccff8dcecb45
diff --git a/sdk/lib/sys/cpp/testing/enclosing_environment.cc b/sdk/lib/sys/cpp/testing/enclosing_environment.cc
index 68609f1..7cabeeb 100644
--- a/sdk/lib/sys/cpp/testing/enclosing_environment.cc
+++ b/sdk/lib/sys/cpp/testing/enclosing_environment.cc
@@ -113,6 +113,14 @@
                 singleton_services_.erase(singleton_id);
               });
 
+          controller.events().OnTerminated =
+              [this, singleton_id](int64_t exit_code,
+                                   fuchsia::sys::TerminationReason reason) {
+                if (service_terminated_callback_) {
+                  service_terminated_callback_(singleton_id, exit_code, reason);
+                }
+              };
+
           std::tie(it, std::ignore) =
               singleton_services_.emplace(singleton_id, std::move(services));
         }
diff --git a/sdk/lib/sys/cpp/testing/enclosing_environment.h b/sdk/lib/sys/cpp/testing/enclosing_environment.h
index fdd0800..0245165 100644
--- a/sdk/lib/sys/cpp/testing/enclosing_environment.h
+++ b/sdk/lib/sys/cpp/testing/enclosing_environment.h
@@ -5,10 +5,6 @@
 #ifndef LIB_SYS_CPP_TESTING_ENCLOSING_ENVIRONMENT_H_
 #define LIB_SYS_CPP_TESTING_ENCLOSING_ENVIRONMENT_H_
 
-#include <memory>
-#include <string>
-#include <unordered_map>
-
 #include <fuchsia/io/cpp/fidl.h>
 #include <fuchsia/sys/cpp/fidl.h>
 #include <lib/async/default.h>
@@ -20,6 +16,10 @@
 #include <lib/vfs/cpp/pseudo_dir.h>
 #include <lib/vfs/cpp/service.h>
 
+#include <memory>
+#include <string>
+#include <unordered_map>
+
 namespace sys {
 namespace testing {
 
@@ -37,6 +37,9 @@
 // created.
 class EnvironmentServices {
  public:
+  using ServiceTerminatedCallback = fit::function<void(
+      const std::string&, int64_t, fuchsia::sys::TerminationReason)>;
+
   struct ParentOverrides final {
     ParentOverrides();
 
@@ -128,18 +131,26 @@
   // Will cause exception if serving fails.
   fidl::InterfaceHandle<fuchsia::io::Directory> ServeServiceDir(
       uint32_t flags = fuchsia::io::OPEN_RIGHT_READABLE |
-                           fuchsia::io::OPEN_RIGHT_WRITABLE);
+                       fuchsia::io::OPEN_RIGHT_WRITABLE);
 
   // Serves service directory using passed |request| and returns status.
   zx_status_t ServeServiceDir(
       fidl::InterfaceRequest<fuchsia::io::Directory> request,
       uint32_t flags = fuchsia::io::OPEN_RIGHT_READABLE |
-                           fuchsia::io::OPEN_RIGHT_WRITABLE);
+                       fuchsia::io::OPEN_RIGHT_WRITABLE);
 
   // Serves service directory using passed |request| and returns status.
   zx_status_t ServeServiceDir(
-      zx::channel request, uint32_t flags =
-          fuchsia::io::OPEN_RIGHT_READABLE | fuchsia::io::OPEN_RIGHT_WRITABLE);
+      zx::channel request, uint32_t flags = fuchsia::io::OPEN_RIGHT_READABLE |
+                                            fuchsia::io::OPEN_RIGHT_WRITABLE);
+
+  // Sets a callback to be triggered whenever a singleton service launched
+  // by |AddServiceWithLaunchInfo| terminates. The callback provides the
+  // singleton identifier (typically fuchsia-pkg URL) and the termination
+  // information.
+  void SetServiceTerminatedCallback(ServiceTerminatedCallback callback) {
+    service_terminated_callback_ = std::move(callback);
+  }
 
  private:
   friend class EnclosingEnvironment;
@@ -155,6 +166,7 @@
   // Pointer to containing environment. Not owned.
   EnclosingEnvironment* enclosing_env_ = nullptr;
   async_dispatcher_t* dispatcher_;
+  ServiceTerminatedCallback service_terminated_callback_;
 
   // Keep track of all singleton services, indexed by url.
   std::unordered_map<std::string, std::shared_ptr<sys::ServiceDirectory>>