M65: Give "service" process its own Service Manager(s)

The cloud print proxy process (i.e. legacy "service" process) may launch
utility processes to do some out-of-process work. These utility
processes behave much like utility processes launched by the browser
process, and occasionally they request an interface from the browser in
order to get some work done.

Font caching behavior on Windows is exposed by the browser and is
sometimes needed from utility processes launched by the cloud print
process, but we don't currently have any way for the cloud print process
to expose interfaces back to utility processes. Such interface requests
will instead be silently ignored.

This CL gives every ServiceUtilityProcessHost its own isolated Service
Manager instance which knows how to route interface requests between the
service process and the single utility process launched by that host.
This in turn allows us to reuse common host interface registration
code to expose some interfaces from the service process.

TBR=rockot@chromium.org

Bug: 813101
Change-Id: I763033215d13535ba545ded7ef7e22fe5484b414
Reviewed-on: https://chromium-review.googlesource.com/924985
Commit-Queue: Ken Rockot <rockot@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Ken Rockot <rockot@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: John Abd-El-Malek <jam@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#538617}
Reviewed-on: https://chromium-review.googlesource.com/933873
Cr-Commit-Position: refs/branch-heads/3325@{#598}
Cr-Branched-From: bc084a8b5afa3744a74927344e304c02ae54189f-refs/heads/master@{#530369}
diff --git a/chrome/service/BUILD.gn b/chrome/service/BUILD.gn
index f4dcb26..291c3bc 100644
--- a/chrome/service/BUILD.gn
+++ b/chrome/service/BUILD.gn
@@ -4,6 +4,7 @@
 
 import("//build/config/features.gni")
 import("//printing/features/features.gni")
+import("//services/catalog/public/tools/catalog.gni")
 
 assert(!is_chromeos)
 
@@ -54,6 +55,7 @@
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
   deps = [
+    ":service_process_catalog_source",
     "//base",
     "//chrome:strings",
     "//chrome/common",
@@ -84,3 +86,15 @@
     ]
   }
 }
+
+catalog("service_process_catalog") {
+  embedded_services = [
+    "//content/public/app:browser_manifest",
+    "//content/public/app:utility_manifest",
+  ]
+}
+
+catalog_cpp_source("service_process_catalog_source") {
+  catalog = ":service_process_catalog"
+  generated_function_name = "CreateServiceProcessCatalog"
+}
diff --git a/chrome/service/DEPS b/chrome/service/DEPS
index 4618fc5..4e2f2a3 100644
--- a/chrome/service/DEPS
+++ b/chrome/service/DEPS
@@ -7,6 +7,5 @@
   "+components/version_info",
   "+mojo/edk/embedder",
   "+sandbox/win/src",
-  "+services/service_manager/public",
-  "+services/service_manager/sandbox",
+  "+services/service_manager",
 ]
diff --git a/chrome/service/service_utility_process_host.cc b/chrome/service/service_utility_process_host.cc
index 4359bb5..d9c29e3 100644
--- a/chrome/service/service_utility_process_host.cc
+++ b/chrome/service/service_utility_process_host.cc
@@ -20,6 +20,7 @@
 #include "base/macros.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/process/launch.h"
+#include "base/process/process_handle.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task_runner_util.h"
@@ -27,13 +28,17 @@
 #include "base/win/win_util.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/chrome_utility_printing_messages.h"
+#include "chrome/service/service_process_catalog_source.h"
 #include "chrome/services/printing/public/interfaces/pdf_to_emf_converter.mojom.h"
 #include "content/public/common/child_process_host.h"
+#include "content/public/common/connection_filter.h"
 #include "content/public/common/content_switches.h"
+#include "content/public/common/font_cache_dispatcher_win.h"
 #include "content/public/common/mojo_channel_switches.h"
 #include "content/public/common/result_codes.h"
 #include "content/public/common/sandbox_init.h"
 #include "content/public/common/sandboxed_process_launcher_delegate.h"
+#include "content/public/common/service_manager_connection.h"
 #include "content/public/common/service_names.mojom.h"
 #include "mojo/edk/embedder/embedder.h"
 #include "mojo/edk/embedder/named_platform_channel_pair.h"
@@ -44,7 +49,14 @@
 #include "printing/emf_win.h"
 #include "sandbox/win/src/sandbox_policy.h"
 #include "sandbox/win/src/sandbox_types.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/interfaces/constants.mojom.h"
+#include "services/service_manager/public/interfaces/service.mojom.h"
+#include "services/service_manager/runner/host/service_process_launcher.h"
+#include "services/service_manager/runner/host/service_process_launcher_factory.h"
 #include "services/service_manager/sandbox/sandbox_type.h"
+#include "services/service_manager/service_manager.h"
 #include "ui/base/ui_base_switches.h"
 
 namespace {
@@ -116,6 +128,49 @@
   mojo::Binding<printing::mojom::PdfToEmfConverterClient> binding_;
 };
 
+class NullServiceProcessLauncherFactory
+    : public service_manager::ServiceProcessLauncherFactory {
+ public:
+  NullServiceProcessLauncherFactory() = default;
+  ~NullServiceProcessLauncherFactory() override = default;
+
+  // service_manager::ServiceProcessLauncherFactory:
+  std::unique_ptr<service_manager::ServiceProcessLauncher> Create(
+      const base::FilePath& service_path) override {
+    LOG(ERROR) << "Attempting to run unsupported native service: "
+               << service_path.value();
+    return nullptr;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NullServiceProcessLauncherFactory);
+};
+
+class ConnectionFilterImpl : public content::ConnectionFilter {
+ public:
+  ConnectionFilterImpl() {
+    registry_.AddInterface(
+        base::BindRepeating(&content::FontCacheDispatcher::Create));
+  }
+
+  ~ConnectionFilterImpl() override = default;
+
+  // content::ConnectionFilter:
+  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
+                       const std::string& interface_name,
+                       mojo::ScopedMessagePipeHandle* interface_pipe,
+                       service_manager::Connector* connector) override {
+    registry_.TryBindInterface(interface_name, interface_pipe, source_info);
+  }
+
+ private:
+  service_manager::BinderRegistryWithArgs<
+      const service_manager::BindSourceInfo&>
+      registry_;
+
+  DISALLOW_COPY_AND_ASSIGN(ConnectionFilterImpl);
+};
+
 }  // namespace
 
 class ServiceUtilityProcessHost::PdfToEmfState {
@@ -303,9 +358,52 @@
     return false;
   }
 
+  // We set up a Service Manager for each utility process hosted here. The
+  // utility processes launched by the service process only ever need to
+  // communicate back to the service process itself, so it's safe to host each
+  // one using its own isolated service manager.
+  //
+  // We do this because some common child process code expects to be connected
+  // to a well-behaved service manager from which it can request common host
+  // interfaces.
+  //
+  // In the isolated service environment there are exactly two service
+  // instances: this process, which masquerades as "content_browser"; and the
+  // child process, which exists ostensibly as the only instance of
+  // "content_utility". This is all set up here.
+  service_manager_ = std::make_unique<service_manager::ServiceManager>(
+      std::make_unique<NullServiceProcessLauncherFactory>(),
+      CreateServiceProcessCatalog(), nullptr);
+
+  service_manager::mojom::ServicePtr browser_proxy;
+  service_manager_connection_ = content::ServiceManagerConnection::Create(
+      mojo::MakeRequest(&browser_proxy),
+      base::SequencedTaskRunnerHandle::Get());
+  service_manager_connection_->AddConnectionFilter(
+      std::make_unique<ConnectionFilterImpl>());
+
+  service_manager::mojom::PIDReceiverPtr pid_receiver;
+  service_manager_->RegisterService(
+      service_manager::Identity(content::mojom::kBrowserServiceName,
+                                service_manager::mojom::kRootUserID),
+      std::move(browser_proxy), mojo::MakeRequest(&pid_receiver));
+  pid_receiver->SetPID(base::GetCurrentProcId());
+  pid_receiver.reset();
+
   std::string mojo_bootstrap_token = mojo::edk::GenerateRandomToken();
-  utility_process_connection_.Bind(service_manager::mojom::ServicePtrInfo(
+  service_manager::mojom::ServicePtr utility_service;
+  utility_service.Bind(service_manager::mojom::ServicePtrInfo(
       broker_client_invitation_.AttachMessagePipe(mojo_bootstrap_token), 0u));
+  service_manager_->RegisterService(
+      service_manager::Identity(content::mojom::kUtilityServiceName,
+                                service_manager::mojom::kRootUserID),
+      std::move(utility_service), mojo::MakeRequest(&pid_receiver));
+  pid_receiver->SetPID(base::GetCurrentProcId());
+
+  service_manager_connection_->Start();
+
+  // NOTE: This call to |CreateChannelMojo()| requires a working
+  // ServiceManagerConnection to have already been established.
   child_process_host_->CreateChannelMojo();
 
   base::CommandLine cmd_line(exe_path);
@@ -414,14 +512,9 @@
 void ServiceUtilityProcessHost::BindInterface(
     const std::string& interface_name,
     mojo::ScopedMessagePipeHandle interface_pipe) {
-  service_manager::BindSourceInfo source_info;
-  // ChildThreadImpl expects a connection from the browser process for
-  // establishing its legacy IPC channel.
-  source_info.identity =
-      service_manager::Identity{content::mojom::kBrowserServiceName};
-  utility_process_connection_->OnBindInterface(source_info, interface_name,
-                                               std::move(interface_pipe),
-                                               base::Bind(&base::DoNothing));
+  service_manager_connection_->GetConnector()->BindInterface(
+      service_manager::Identity(content::mojom::kUtilityServiceName),
+      interface_name, std::move(interface_pipe));
 }
 
 void ServiceUtilityProcessHost::OnMetafileSpooled(bool success) {
diff --git a/chrome/service/service_utility_process_host.h b/chrome/service/service_utility_process_host.h
index 61bee76..07a8fa8 100644
--- a/chrome/service/service_utility_process_host.h
+++ b/chrome/service/service_utility_process_host.h
@@ -15,7 +15,6 @@
 #include "content/public/common/child_process_host_delegate.h"
 #include "ipc/ipc_platform_file.h"
 #include "mojo/edk/embedder/outgoing_broker_client_invitation.h"
-#include "services/service_manager/public/interfaces/service.mojom.h"
 
 namespace base {
 class CommandLine;
@@ -26,6 +25,7 @@
 
 namespace content {
 class ChildProcessHost;
+class ServiceManagerConnection;
 }
 
 namespace printing {
@@ -35,6 +35,10 @@
 struct PrinterSemanticCapsAndDefaults;
 }  // namespace printing
 
+namespace service_manager {
+class ServiceManager;
+}
+
 // Acts as the service-side host to a utility child process. A
 // utility process is a short-lived sandboxed process that is created to run
 // a specific task.
@@ -161,7 +165,9 @@
   class PdfToEmfState;
   std::unique_ptr<PdfToEmfState> pdf_to_emf_state_;
 
-  service_manager::mojom::ServicePtr utility_process_connection_;
+  std::unique_ptr<service_manager::ServiceManager> service_manager_;
+  std::unique_ptr<content::ServiceManagerConnection>
+      service_manager_connection_;
 
   base::WeakPtrFactory<ServiceUtilityProcessHost> weak_ptr_factory_;
 
diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc
index a9dc129..216d379 100644
--- a/content/browser/renderer_host/render_message_filter.cc
+++ b/content/browser/renderer_host/render_message_filter.cc
@@ -67,7 +67,7 @@
 #include "url/origin.h"
 
 #if defined(OS_WIN)
-#include "content/common/font_cache_dispatcher_win.h"
+#include "content/public/common/font_cache_dispatcher_win.h"
 #endif
 
 #if defined(OS_POSIX)
diff --git a/content/browser/service_manager/common_browser_interfaces.cc b/content/browser/service_manager/common_browser_interfaces.cc
index caa6252..2517049 100644
--- a/content/browser/service_manager/common_browser_interfaces.cc
+++ b/content/browser/service_manager/common_browser_interfaces.cc
@@ -24,7 +24,7 @@
 
 #if defined(OS_WIN)
 #include "content/browser/renderer_host/dwrite_font_proxy_message_filter_win.h"
-#include "content/common/font_cache_dispatcher_win.h"
+#include "content/public/common/font_cache_dispatcher_win.h"
 #endif
 
 namespace content {
diff --git a/content/child/child_thread_impl.h b/content/child/child_thread_impl.h
index 54bf250..fa8049f 100644
--- a/content/child/child_thread_impl.h
+++ b/content/child/child_thread_impl.h
@@ -32,7 +32,7 @@
 #include "services/resource_coordinator/public/cpp/tracing/chrome_trace_event_agent.h"
 
 #if defined(OS_WIN)
-#include "content/common/font_cache_win.mojom.h"
+#include "content/public/common/font_cache_win.mojom.h"
 #endif
 
 namespace base {
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index eab8f4d..5648215 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -119,7 +119,6 @@
     "fileapi/file_system_messages.h",
     "fileapi/webblob_messages.h",
     "font_cache_dispatcher_win.cc",
-    "font_cache_dispatcher_win.h",
     "font_config_ipc_linux.cc",
     "font_config_ipc_linux.h",
     "font_list.cc",
@@ -580,10 +579,7 @@
   ]
 
   if (is_win) {
-    sources += [
-      "dwrite_font_proxy.mojom",
-      "font_cache_win.mojom",
-    ]
+    sources += [ "dwrite_font_proxy.mojom" ]
   }
 
   import_dirs = [ "//mojo/services" ]
diff --git a/content/common/font_cache_dispatcher_win.cc b/content/common/font_cache_dispatcher_win.cc
index 2f6e592..9337781 100644
--- a/content/common/font_cache_dispatcher_win.cc
+++ b/content/common/font_cache_dispatcher_win.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/common/font_cache_dispatcher_win.h"
+#include "content/public/common/font_cache_dispatcher_win.h"
 
 #include <map>
 #include <memory>
diff --git a/content/public/common/BUILD.gn b/content/public/common/BUILD.gn
index 3898737..05cce08 100644
--- a/content/public/common/BUILD.gn
+++ b/content/public/common/BUILD.gn
@@ -147,6 +147,7 @@
     "file_chooser_file_info.h",
     "file_chooser_params.cc",
     "file_chooser_params.h",
+    "font_cache_dispatcher_win.h",
     "frame_navigate_params.cc",
     "frame_navigate_params.h",
     "injection_test_mac.h",
@@ -374,6 +375,10 @@
     "window_container_type.mojom",
   ]
 
+  if (is_win) {
+    sources += [ "font_cache_win.mojom" ]
+  }
+
   public_deps = [
     ":resource_type_bindings",
     "//mojo/common:common_custom_types",
diff --git a/content/common/font_cache_dispatcher_win.h b/content/public/common/font_cache_dispatcher_win.h
similarity index 87%
rename from content/common/font_cache_dispatcher_win.h
rename to content/public/common/font_cache_dispatcher_win.h
index 29eaa48..c046fda 100644
--- a/content/common/font_cache_dispatcher_win.h
+++ b/content/public/common/font_cache_dispatcher_win.h
@@ -9,7 +9,8 @@
 
 #include "base/macros.h"
 #include "base/memory/singleton.h"
-#include "content/common/font_cache_win.mojom.h"
+#include "content/common/content_export.h"
+#include "content/public/common/font_cache_win.mojom.h"
 
 namespace service_manager {
 struct BindSourceInfo;
@@ -20,7 +21,7 @@
 // Dispatches messages used for font caching on Windows. This is needed because
 // Windows can't load fonts into its kernel cache in sandboxed processes. So the
 // sandboxed process asks the browser process to do this for it.
-class FontCacheDispatcher : public mojom::FontCacheWin {
+class CONTENT_EXPORT FontCacheDispatcher : public mojom::FontCacheWin {
  public:
   FontCacheDispatcher();
   ~FontCacheDispatcher() override;
diff --git a/content/common/font_cache_win.mojom b/content/public/common/font_cache_win.mojom
similarity index 100%
rename from content/common/font_cache_win.mojom
rename to content/public/common/font_cache_win.mojom