[CrOS MultiDevice] Create a skeleton MultiDeviceSetup service.

See go/unified-better-together-cros for an outline of this service.

This CL does the following:
(1) Creates a Mojo interface for the service, adding only a single
    function for now.
(2) Creates the MultiDeviceSetupService and MultiDeviceSetupImpl classes
    which implement this interface, as well as a test for the service.
(3) Registers the service in the browser process via ProfileImpl.
(4) Generates JavaScript files for the interface; because these files
    are the first resources generated in //chromeos, this CL also adds a
    new chromeos_resources.grd.
(5) Adds the new "multidevice_setup" capability to the content_browser
    manifest file so that it can be used by content_renderer.
(6) Adds a new button on the chrome://proximity-auth debug WebUI page
    which makes a Mojo call to the service.

Change-Id: I0b74373b78a1c92fe01d789028342707321c2134
Reviewed-on: https://chromium-review.googlesource.com/954010
Reviewed-by: Steven Bennetts <[email protected]>
Reviewed-by: Daniel Cheng <[email protected]>
Reviewed-by: agrieve <[email protected]>
Reviewed-by: anthonyvd <[email protected]>
Reviewed-by: Ken Rockot <[email protected]>
Commit-Queue: Kyle Horimoto <[email protected]>
Cr-Commit-Position: refs/heads/master@{#543894}
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index f4c072e4..e8a76552 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -644,8 +644,8 @@
     ":power_manager_proto",
     ":test_support",
     ":test_support_without_gmock",
-    "//base/test:run_all_unittests",
     "//base/test:test_support",
+    "//chromeos/services:unit_tests",
     "//components/onc",
     "//components/prefs:test_support",
     "//components/proxy_config",
@@ -655,6 +655,7 @@
     "//dbus:test_support",
     "//google_apis",
     "//media/base:video_facing",
+    "//mojo/edk",
     "//net",
     "//net:test_support",
     "//testing/gmock",
@@ -736,6 +737,7 @@
     "printing/printer_translator_unittest.cc",
     "process_proxy/process_output_watcher_unittest.cc",
     "process_proxy/process_proxy_unittest.cc",
+    "run_all_unittests.cc",
     "settings/timezone_settings_unittest.cc",
     "system/cpu_temperature_reader_unittest.cc",
     "system/name_value_pairs_parser_unittest.cc",
diff --git a/chromeos/DEPS b/chromeos/DEPS
index abc781f..775ac1a9 100644
--- a/chromeos/DEPS
+++ b/chromeos/DEPS
@@ -10,6 +10,7 @@
   "+components/user_manager/known_user.h",
   "+crypto",
   "+media/base/video_facing.h",
+  "+mojo/edk/embedder/embedder.h",
   "+net",
   "+third_party/cros_system_api",
   "+third_party/protobuf",
diff --git a/chromeos/components/BUILD.gn b/chromeos/components/BUILD.gn
index 7b111253..636f4bc 100644
--- a/chromeos/components/BUILD.gn
+++ b/chromeos/components/BUILD.gn
@@ -16,7 +16,6 @@
   deps = [
     "//base",
     "//base/test:test_support",
-    "//chromeos/components/multidevice_setup:unit_tests",
     "//chromeos/components/tether:unit_tests",
   ]
 }
diff --git a/chromeos/components/multidevice_setup/BUILD.gn b/chromeos/components/multidevice_setup/BUILD.gn
deleted file mode 100644
index 12b843b..0000000
--- a/chromeos/components/multidevice_setup/BUILD.gn
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright 2018 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos")
-
-static_library("multidevice_setup") {
-  sources = []
-
-  deps = [
-    "//base",
-  ]
-}
-
-static_library("test_support") {
-  testonly = true
-
-  sources = []
-
-  public_deps = [
-    ":multidevice_setup",
-  ]
-
-  deps = [
-    "//base",
-    "//testing/gmock",
-    "//testing/gtest",
-  ]
-}
-
-source_set("unit_tests") {
-  testonly = true
-
-  sources = []
-
-  deps = [
-    ":multidevice_setup",
-    ":test_support",
-    "//base/test:test_support",
-    "//testing/gmock",
-    "//testing/gtest",
-  ]
-}
diff --git a/chromeos/components/multidevice_setup/DEPS b/chromeos/components/multidevice_setup/DEPS
deleted file mode 100644
index 48e88750..0000000
--- a/chromeos/components/multidevice_setup/DEPS
+++ /dev/null
@@ -1,2 +0,0 @@
-include_rules = [
-]
diff --git a/chromeos/components/multidevice_setup/OWNERS b/chromeos/components/multidevice_setup/OWNERS
deleted file mode 100644
index 1d672ca..0000000
--- a/chromeos/components/multidevice_setup/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
[email protected]
[email protected]
-
-# COMPONENT: UI>ProximityAuth
diff --git a/chromeos/resources/BUILD.gn b/chromeos/resources/BUILD.gn
new file mode 100644
index 0000000..4b64e4e5
--- /dev/null
+++ b/chromeos/resources/BUILD.gn
@@ -0,0 +1,28 @@
+# Copyright 2018 The Chromium 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("//tools/grit/grit_rule.gni")
+
+assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos")
+
+grit("resources") {
+  source = "chromeos_resources.grd"
+
+  source_is_generated = true
+
+  outputs = [
+    "grit/chromeos_resources.h",
+    "chromeos_resources.pak",
+  ]
+  output_dir = "$root_gen_dir/chromeos"
+
+  grit_flags = [
+    "-E",
+    "mojom_root=" + rebase_path(root_gen_dir, root_build_dir),
+  ]
+
+  deps = [
+    "//chromeos/services/multidevice_setup/public/mojom:mojom_js",
+  ]
+}
diff --git a/chromeos/resources/chromeos_resources.grd b/chromeos/resources/chromeos_resources.grd
new file mode 100644
index 0000000..169869e
--- /dev/null
+++ b/chromeos/resources/chromeos_resources.grd
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<grit latest_public_release="0" current_release="1" output_all_resource_defines="false">
+  <outputs>
+    <output filename="grit/chromeos_resources.h" type="rc_header">
+      <emit emit_type='prepend'></emit>
+    </output>
+    <output filename="chromeos_resources.pak" type="data_package" />
+  </outputs>
+  <release seq="1">
+    <includes>
+      <include name="IDR_MULTIDEVICE_SETUP_MOJOM_JS"
+          file="${mojom_root}/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.js"
+          use_base_dir="false"
+          type="BINDATA" />
+      <include name="IDR_MULTIDEVICE_SETUP_CONSTANTS_MOJOM_JS"
+          file="${mojom_root}/chromeos/services/multidevice_setup/public/mojom/constants.mojom.js"
+          use_base_dir="false"
+          type="BINDATA" />
+    </includes>
+  </release>
+</grit>
diff --git a/chromeos/run_all_unittests.cc b/chromeos/run_all_unittests.cc
new file mode 100644
index 0000000..3efc1c0
--- /dev/null
+++ b/chromeos/run_all_unittests.cc
@@ -0,0 +1,18 @@
+// Copyright 2018 The Chromium 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 "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "mojo/edk/embedder/embedder.h"
+
+int main(int argc, char** argv) {
+  // Some unit tests make Mojo calls.
+  mojo::edk::Init();
+
+  base::TestSuite test_suite(argc, argv);
+  return base::LaunchUnitTests(
+      argc, argv,
+      base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/chromeos/services/BUILD.gn b/chromeos/services/BUILD.gn
index 5e25e1ff..32bf290 100644
--- a/chromeos/services/BUILD.gn
+++ b/chromeos/services/BUILD.gn
@@ -8,14 +8,29 @@
 import("//services/service_manager/public/tools/test/service_test.gni")
 import("//testing/test.gni")
 
-# This is modeled after //services:services_unittests
-# One Big Target for services to register their unit test sources. This exists
-# to avoid having to maintain a separate test binary for every service.
+assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos")
+
+# Use this target for adding new unit tests. To add a unit test to this target,
+# create a "unit_tests" source_set in your service and add it as a dependency
+# here.
 #
-# To add tests for a new service, please define a "tests" source_set in the
-# service subdirectory and add it as a dependency here. If your unit tests
-# use the ServiceTest framework, you must also include corresponding catalog
-# entries in the "chromeos_services_unittests_catalog" target below.
+# Unit tests are generally preferred over service tests as they are simpler to
+# create and maintain. Check out service_manager::TestConnectorFactory for an
+# easy way to test your services.
+source_set("unit_tests") {
+  testonly = true
+  deps = [
+    "//chromeos/services/multidevice_setup:unit_tests",
+  ]
+}
+
+# Use this target for adding new service tests. To add a unit test to this
+# target, create a "tests" source_set in your service and add it as a dependency
+# here.
+#
+# If your unit tests use the ServiceTest framework, you must also include
+# corresponding catalog entries in the "chromeos_services_unittests_catalog"
+# target below.
 service_test("chromeos_services_unittests") {
   deps = []
 
diff --git a/chromeos/services/DEPS b/chromeos/services/DEPS
new file mode 100644
index 0000000..a8e1150f
--- /dev/null
+++ b/chromeos/services/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo/edk/embedder/embedder.h"
+]
diff --git a/chromeos/services/multidevice_setup/BUILD.gn b/chromeos/services/multidevice_setup/BUILD.gn
new file mode 100644
index 0000000..c26c0db
--- /dev/null
+++ b/chromeos/services/multidevice_setup/BUILD.gn
@@ -0,0 +1,69 @@
+# Copyright 2018 The Chromium 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("//services/service_manager/public/cpp/service.gni")
+import("//services/service_manager/public/service_manifest.gni")
+import("//services/service_manager/public/tools/test/service_test.gni")
+
+assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos")
+
+static_library("multidevice_setup") {
+  sources = [
+    "multidevice_setup_impl.cc",
+    "multidevice_setup_impl.h",
+    "multidevice_setup_service.cc",
+    "multidevice_setup_service.h",
+  ]
+
+  deps = [
+    "//base",
+    "//chromeos/services/multidevice_setup/public/mojom",
+    "//components/proximity_auth/logging",
+    "//services/service_manager/public/cpp",
+  ]
+}
+
+service_manifest("manifest") {
+  name = "multidevice_setup"
+  source = "manifest.json"
+}
+
+static_library("test_support") {
+  testonly = true
+
+  sources = [
+    "fake_multidevice_setup_observer.cc",
+    "fake_multidevice_setup_observer.h",
+  ]
+
+  public_deps = [
+    ":multidevice_setup",
+  ]
+
+  deps = [
+    ":multidevice_setup",
+    "//base",
+    "//chromeos/services/multidevice_setup/public/mojom",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [
+    "multidevice_setup_service_unittest.cc",
+  ]
+
+  deps = [
+    ":multidevice_setup",
+    ":test_support",
+    "//base/test:test_support",
+    "//chromeos/services/multidevice_setup/public/mojom",
+    "//services/service_manager/public/cpp/test:test_support",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/chromeos/services/multidevice_setup/DEPS b/chromeos/services/multidevice_setup/DEPS
new file mode 100644
index 0000000..5f70575e
--- /dev/null
+++ b/chromeos/services/multidevice_setup/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+components/proximity_auth/logging/logging.h",
+  "+mojo/public/cpp/bindings/binding_set.h",
+  "+services/service_manager/public",
+]
diff --git a/chromeos/services/multidevice_setup/OWNERS b/chromeos/services/multidevice_setup/OWNERS
new file mode 100644
index 0000000..8b584e9
--- /dev/null
+++ b/chromeos/services/multidevice_setup/OWNERS
@@ -0,0 +1,7 @@
[email protected]
[email protected]
+
+per-file manifest.json=set noparent
+per-file manifest.json=file://ipc/SECURITY_OWNERS
+
+# COMPONENT: UI>ProximityAuth
diff --git a/chromeos/services/multidevice_setup/fake_multidevice_setup_observer.cc b/chromeos/services/multidevice_setup/fake_multidevice_setup_observer.cc
new file mode 100644
index 0000000..7dc544b
--- /dev/null
+++ b/chromeos/services/multidevice_setup/fake_multidevice_setup_observer.cc
@@ -0,0 +1,36 @@
+// Copyright 2018 The Chromium 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 "chromeos/services/multidevice_setup/fake_multidevice_setup_observer.h"
+
+namespace chromeos {
+
+namespace multidevice {
+
+FakeMultiDeviceSetupObserver::FakeMultiDeviceSetupObserver() = default;
+
+FakeMultiDeviceSetupObserver::~FakeMultiDeviceSetupObserver() = default;
+
+multidevice_setup::mojom::MultiDeviceSetupObserverPtr
+FakeMultiDeviceSetupObserver::GenerateInterfacePtr() {
+  multidevice_setup::mojom::MultiDeviceSetupObserverPtr interface_ptr;
+  bindings_.AddBinding(this, mojo::MakeRequest(&interface_ptr));
+  return interface_ptr;
+}
+
+void FakeMultiDeviceSetupObserver::OnPotentialHostExistsForNewUser() {
+  ++num_new_user_events_handled_;
+}
+
+void FakeMultiDeviceSetupObserver::OnConnectedHostSwitchedForExistingUser() {
+  ++num_existing_user_host_switched_events_handled_;
+}
+
+void FakeMultiDeviceSetupObserver::OnNewChromebookAddedForExistingUser() {
+  ++num_existing_user_chromebook_added_events_handled_;
+}
+
+}  // namespace multidevice
+
+}  // namespace chromeos
diff --git a/chromeos/services/multidevice_setup/fake_multidevice_setup_observer.h b/chromeos/services/multidevice_setup/fake_multidevice_setup_observer.h
new file mode 100644
index 0000000..a62b14ba
--- /dev/null
+++ b/chromeos/services/multidevice_setup/fake_multidevice_setup_observer.h
@@ -0,0 +1,55 @@
+// Copyright 2018 The Chromium 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 CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FAKE_MULTIDEVICE_SETUP_OBSERVER_H_
+#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FAKE_MULTIDEVICE_SETUP_OBSERVER_H_
+
+#include "base/macros.h"
+#include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+
+namespace chromeos {
+
+namespace multidevice {
+
+// Fake MultiDeviceSetupObserver implementation for tests.
+class FakeMultiDeviceSetupObserver
+    : public multidevice_setup::mojom::MultiDeviceSetupObserver {
+ public:
+  FakeMultiDeviceSetupObserver();
+  ~FakeMultiDeviceSetupObserver() override;
+
+  multidevice_setup::mojom::MultiDeviceSetupObserverPtr GenerateInterfacePtr();
+
+  size_t num_new_user_events_handled() { return num_new_user_events_handled_; }
+
+  size_t num_existing_user_host_switched_events_handled() {
+    return num_existing_user_host_switched_events_handled_;
+  }
+
+  size_t num_existing_user_chromebook_added_events_handled() {
+    return num_existing_user_chromebook_added_events_handled_;
+  }
+
+  // multidevice_setup::mojom::MultiDeviceSetupObserver:
+  void OnPotentialHostExistsForNewUser() override;
+  void OnConnectedHostSwitchedForExistingUser() override;
+  void OnNewChromebookAddedForExistingUser() override;
+
+ private:
+  size_t num_new_user_events_handled_ = 0u;
+  size_t num_existing_user_host_switched_events_handled_ = 0u;
+  size_t num_existing_user_chromebook_added_events_handled_ = 0u;
+
+  mojo::BindingSet<multidevice_setup::mojom::MultiDeviceSetupObserver>
+      bindings_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeMultiDeviceSetupObserver);
+};
+
+}  // namespace multidevice
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FAKE_MULTIDEVICE_SETUP_OBSERVER_H_
diff --git a/chromeos/services/multidevice_setup/manifest.json b/chromeos/services/multidevice_setup/manifest.json
new file mode 100644
index 0000000..bb20ab69
--- /dev/null
+++ b/chromeos/services/multidevice_setup/manifest.json
@@ -0,0 +1,11 @@
+{
+  "name": "multidevice_setup",
+  "display_name": "MultiDevice Setup Service",
+  "interface_provider_specs": {
+    "service_manager:connector": {
+      "provides": {
+        "multidevice_setup" : [ "multidevice_setup::mojom::MultiDeviceSetup" ]
+      }
+    }
+  }
+}
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl.cc b/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
new file mode 100644
index 0000000..5d8a5e3
--- /dev/null
+++ b/chromeos/services/multidevice_setup/multidevice_setup_impl.cc
@@ -0,0 +1,88 @@
+// Copyright 2018 The Chromium 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 "chromeos/services/multidevice_setup/multidevice_setup_impl.h"
+
+#include "components/proximity_auth/logging/logging.h"
+
+namespace chromeos {
+
+namespace multidevice {
+
+namespace {
+
+using EventType = multidevice_setup::mojom::EventTypeForDebugging;
+
+std::string EventTypeEnumToString(EventType type) {
+  switch (type) {
+    case EventType::kNewUserPotentialHostExists:
+      return "kNewUserPotentialHostExists";
+    case EventType::kExistingUserConnectedHostSwitched:
+      return "kExistingUserConnectedHostSwitched";
+    case EventType::kExistingUserNewChromebookAdded:
+      return "kExistingUserNewChromebookAdded";
+    default:
+      NOTREACHED();
+      return "[invalid input]";
+  }
+}
+
+}  // namespace
+
+MultiDeviceSetupImpl::MultiDeviceSetupImpl() = default;
+
+MultiDeviceSetupImpl::~MultiDeviceSetupImpl() = default;
+
+void MultiDeviceSetupImpl::BindRequest(
+    multidevice_setup::mojom::MultiDeviceSetupRequest request) {
+  bindings_.AddBinding(this, std::move(request));
+}
+
+void MultiDeviceSetupImpl::SetObserver(
+    multidevice_setup::mojom::MultiDeviceSetupObserverPtr observer,
+    SetObserverCallback callback) {
+  if (observer_.is_bound()) {
+    PA_LOG(ERROR) << "SetObserver() called when a MultiDeviceSetupObserver was "
+                  << "already set. Replacing the previously-set "
+                  << "MultiDeviceSetupObserver.";
+  }
+
+  PA_LOG(INFO) << "MultiDeviceSetupImpl::SetObserver()";
+  observer_ = std::move(observer);
+  std::move(callback).Run();
+}
+
+void MultiDeviceSetupImpl::TriggerEventForDebugging(
+    EventType type,
+    TriggerEventForDebuggingCallback callback) {
+  PA_LOG(INFO) << "TriggerEventForDebugging(" << EventTypeEnumToString(type)
+               << ") called.";
+
+  if (!observer_.is_bound()) {
+    PA_LOG(ERROR) << "No MultiDeviceSetupObserver has been set. Cannot "
+                  << "proceed.";
+    std::move(callback).Run(false /* success */);
+    return;
+  }
+
+  switch (type) {
+    case EventType::kNewUserPotentialHostExists:
+      observer_->OnPotentialHostExistsForNewUser();
+      break;
+    case EventType::kExistingUserConnectedHostSwitched:
+      observer_->OnConnectedHostSwitchedForExistingUser();
+      break;
+    case EventType::kExistingUserNewChromebookAdded:
+      observer_->OnNewChromebookAddedForExistingUser();
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  std::move(callback).Run(true /* success */);
+}
+
+}  // namespace multidevice
+
+}  // namespace chromeos
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_impl.h b/chromeos/services/multidevice_setup/multidevice_setup_impl.h
new file mode 100644
index 0000000..7d4d3163
--- /dev/null
+++ b/chromeos/services/multidevice_setup/multidevice_setup_impl.h
@@ -0,0 +1,48 @@
+// Copyright 2018 The Chromium 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 CHROMEOS_SERVICES_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_IMPL_H_
+#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_IMPL_H_
+
+#include <memory>
+
+#include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/cpp/service.h"
+
+namespace chromeos {
+
+namespace multidevice {
+
+// Concrete MultiDeviceSetup implementation.
+class MultiDeviceSetupImpl : public multidevice_setup::mojom::MultiDeviceSetup {
+ public:
+  MultiDeviceSetupImpl();
+  ~MultiDeviceSetupImpl() override;
+
+  // Binds a request to this implementation. Should be called each time that the
+  // service receives a request.
+  void BindRequest(multidevice_setup::mojom::MultiDeviceSetupRequest request);
+
+  // multidevice_setup::mojom::MultiDeviceSetup:
+  void SetObserver(
+      multidevice_setup::mojom::MultiDeviceSetupObserverPtr presenter,
+      SetObserverCallback callback) override;
+  void TriggerEventForDebugging(
+      multidevice_setup::mojom::EventTypeForDebugging type,
+      TriggerEventForDebuggingCallback callback) override;
+
+ private:
+  multidevice_setup::mojom::MultiDeviceSetupObserverPtr observer_;
+  mojo::BindingSet<multidevice_setup::mojom::MultiDeviceSetup> bindings_;
+
+  DISALLOW_COPY_AND_ASSIGN(MultiDeviceSetupImpl);
+};
+
+}  // namespace multidevice
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_IMPL_H_
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_service.cc b/chromeos/services/multidevice_setup/multidevice_setup_service.cc
new file mode 100644
index 0000000..79b80d3b
--- /dev/null
+++ b/chromeos/services/multidevice_setup/multidevice_setup_service.cc
@@ -0,0 +1,41 @@
+// Copyright 2018 The Chromium 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 "chromeos/services/multidevice_setup/multidevice_setup_service.h"
+
+#include "chromeos/services/multidevice_setup/multidevice_setup_impl.h"
+#include "components/proximity_auth/logging/logging.h"
+
+namespace chromeos {
+
+namespace multidevice {
+
+MultiDeviceSetupService::MultiDeviceSetupService()
+    : multidevice_setup_impl_(std::make_unique<MultiDeviceSetupImpl>()) {}
+
+MultiDeviceSetupService::~MultiDeviceSetupService() = default;
+
+void MultiDeviceSetupService::OnStart() {
+  PA_LOG(INFO) << "MultiDeviceSetupService::OnStart()";
+  registry_.AddInterface(base::Bind(&MultiDeviceSetupService::BindRequest,
+                                    base::Unretained(this)));
+}
+
+void MultiDeviceSetupService::OnBindInterface(
+    const service_manager::BindSourceInfo& source_info,
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle interface_pipe) {
+  PA_LOG(INFO) << "MultiDeviceSetupService::OnBindInterface() from interface "
+               << interface_name << ".";
+  registry_.BindInterface(interface_name, std::move(interface_pipe));
+}
+
+void MultiDeviceSetupService::BindRequest(
+    multidevice_setup::mojom::MultiDeviceSetupRequest request) {
+  multidevice_setup_impl_->BindRequest(std::move(request));
+}
+
+}  // namespace multidevice
+
+}  // namespace chromeos
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_service.h b/chromeos/services/multidevice_setup/multidevice_setup_service.h
new file mode 100644
index 0000000..8a1b8fb
--- /dev/null
+++ b/chromeos/services/multidevice_setup/multidevice_setup_service.h
@@ -0,0 +1,48 @@
+// Copyright 2018 The Chromium 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 CHROMEOS_SERVICES_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_SERVICE_H_
+#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_SERVICE_H_
+
+#include <memory>
+
+#include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/cpp/service.h"
+
+namespace chromeos {
+
+namespace multidevice {
+
+class MultiDeviceSetupImpl;
+
+// Service which provides an implementation for
+// multidevice_setup::mojom::MultiDeviceSetup. This service creates one
+// implementation and shares it among all connection requests.
+class MultiDeviceSetupService : public service_manager::Service {
+ public:
+  MultiDeviceSetupService();
+  ~MultiDeviceSetupService() override;
+
+ private:
+  // service_manager::Service:
+  void OnStart() override;
+  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
+                       const std::string& interface_name,
+                       mojo::ScopedMessagePipeHandle interface_pipe) override;
+
+  void BindRequest(multidevice_setup::mojom::MultiDeviceSetupRequest request);
+
+  std::unique_ptr<MultiDeviceSetupImpl> multidevice_setup_impl_;
+
+  service_manager::BinderRegistry registry_;
+
+  DISALLOW_COPY_AND_ASSIGN(MultiDeviceSetupService);
+};
+
+}  // namespace multidevice
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_SERVICE_H_
diff --git a/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc b/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc
new file mode 100644
index 0000000..aaabf71
--- /dev/null
+++ b/chromeos/services/multidevice_setup/multidevice_setup_service_unittest.cc
@@ -0,0 +1,126 @@
+// Copyright 2018 The Chromium 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 <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "chromeos/services/multidevice_setup/fake_multidevice_setup_observer.h"
+#include "chromeos/services/multidevice_setup/multidevice_setup_service.h"
+#include "chromeos/services/multidevice_setup/public/mojom/constants.mojom.h"
+#include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
+#include "services/service_manager/public/cpp/test/test_connector_factory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace multidevice {
+
+using EventType = multidevice_setup::mojom::EventTypeForDebugging;
+
+class MultiDeviceSetupServiceTest : public testing::Test {
+ protected:
+  MultiDeviceSetupServiceTest() = default;
+  ~MultiDeviceSetupServiceTest() override = default;
+
+  void SetUp() override {
+    fake_multidevice_setup_observer_ =
+        std::make_unique<FakeMultiDeviceSetupObserver>();
+    connector_factory_ =
+        service_manager::TestConnectorFactory::CreateForUniqueService(
+            std::make_unique<MultiDeviceSetupService>());
+  }
+
+  multidevice_setup::mojom::MultiDeviceSetup* GetMultiDeviceSetup() {
+    if (!multidevice_setup_) {
+      EXPECT_EQ(nullptr, connector_);
+
+      // Create the Connector and bind it to |multidevice_setup_|.
+      connector_ = connector_factory_->CreateConnector();
+      connector_->BindInterface(multidevice_setup::mojom::kServiceName,
+                                &multidevice_setup_);
+
+      // Set |fake_multidevice_setup_observer_|.
+      CallSetObserver();
+    }
+
+    return multidevice_setup_.get();
+  }
+
+  FakeMultiDeviceSetupObserver* fake_multidevice_setup_observer() {
+    return fake_multidevice_setup_observer_.get();
+  }
+
+  void CallSetObserver() {
+    base::RunLoop run_loop;
+    multidevice_setup_->SetObserver(
+        fake_multidevice_setup_observer_->GenerateInterfacePtr(),
+        base::BindRepeating(
+            &MultiDeviceSetupServiceTest::OnNotificationPresenterRegistered,
+            base::Unretained(this), run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+
+  void CallTriggerEventForDebugging(EventType type) {
+    base::RunLoop run_loop;
+    GetMultiDeviceSetup()->TriggerEventForDebugging(
+        type, base::BindRepeating(
+                  &MultiDeviceSetupServiceTest::OnNotificationTriggered,
+                  base::Unretained(this), run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+
+ private:
+  void OnNotificationPresenterRegistered(
+      const base::RepeatingClosure& quit_closure) {
+    quit_closure.Run();
+  }
+
+  void OnNotificationTriggered(const base::RepeatingClosure& quit_closure,
+                               bool success) {
+    // NotificationPresenter is set in GetMultiDeviceSetup().
+    EXPECT_TRUE(success);
+    quit_closure.Run();
+  }
+
+  const base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  std::unique_ptr<service_manager::TestConnectorFactory> connector_factory_;
+  std::unique_ptr<service_manager::Connector> connector_;
+
+  std::unique_ptr<FakeMultiDeviceSetupObserver>
+      fake_multidevice_setup_observer_;
+  multidevice_setup::mojom::MultiDeviceSetupPtr multidevice_setup_;
+
+  DISALLOW_COPY_AND_ASSIGN(MultiDeviceSetupServiceTest);
+};
+
+TEST_F(MultiDeviceSetupServiceTest,
+       TriggerEventForDebugging_kNewUserPotentialHostExists) {
+  CallTriggerEventForDebugging(EventType::kNewUserPotentialHostExists);
+
+  EXPECT_EQ(1u,
+            fake_multidevice_setup_observer()->num_new_user_events_handled());
+}
+
+TEST_F(MultiDeviceSetupServiceTest,
+       TriggerEventForDebugging_kExistingUserConnectedHostSwitched) {
+  CallTriggerEventForDebugging(EventType::kExistingUserConnectedHostSwitched);
+
+  EXPECT_EQ(1u, fake_multidevice_setup_observer()
+                    ->num_existing_user_host_switched_events_handled());
+}
+
+TEST_F(MultiDeviceSetupServiceTest,
+       TriggerEventForDebugging_kExistingUserNewChromebookAdded) {
+  CallTriggerEventForDebugging(EventType::kExistingUserNewChromebookAdded);
+
+  EXPECT_EQ(1u, fake_multidevice_setup_observer()
+                    ->num_existing_user_chromebook_added_events_handled());
+}
+
+}  // namespace multidevice
+
+}  // namespace chromeos
diff --git a/chromeos/services/multidevice_setup/public/mojom/BUILD.gn b/chromeos/services/multidevice_setup/public/mojom/BUILD.gn
new file mode 100644
index 0000000..a123fc6d
--- /dev/null
+++ b/chromeos/services/multidevice_setup/public/mojom/BUILD.gn
@@ -0,0 +1,12 @@
+# Copyright 2018 The Chromium 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("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojom") {
+  sources = [
+    "constants.mojom",
+    "multidevice_setup.mojom",
+  ]
+}
diff --git a/chromeos/services/multidevice_setup/public/mojom/OWNERS b/chromeos/services/multidevice_setup/public/mojom/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/chromeos/services/multidevice_setup/public/mojom/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromeos/services/multidevice_setup/public/mojom/constants.mojom b/chromeos/services/multidevice_setup/public/mojom/constants.mojom
new file mode 100644
index 0000000..ccdc66d
--- /dev/null
+++ b/chromeos/services/multidevice_setup/public/mojom/constants.mojom
@@ -0,0 +1,7 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module multidevice_setup.mojom;
+
+const string kServiceName = "multidevice_setup";
diff --git a/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom b/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom
new file mode 100644
index 0000000..c8da6e8
--- /dev/null
+++ b/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom
@@ -0,0 +1,51 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module multidevice_setup.mojom;
+
+// Handles events dispatched by MultiDeviceSetup.
+interface MultiDeviceSetupObserver {
+  // Callback which indicates that one or more MultiDevice host phones are
+  // available for setup with the MultiDevice setup flow. This function is only
+  // called if the current user has not yet set up MultiDevice features.
+  OnPotentialHostExistsForNewUser();
+
+  // Callback which indicates that the currently-connected MultiDevice host has
+  // changed. This likely means that the user has changed MultiDevice settings
+  // on another device. This function is only called if the current user has
+  // already set up MultiDevice features.
+  OnConnectedHostSwitchedForExistingUser();
+
+  // Callback which indicates that a new Chromebook was added to the account of
+  // the current user. This function is only called if the current user has
+  // already set up MultiDevice features.
+  OnNewChromebookAddedForExistingUser();
+};
+
+// Enumeration of event types which can be dispatched. Only used for debugging
+// purposes.
+enum EventTypeForDebugging {
+  kNewUserPotentialHostExists,
+  kExistingUserConnectedHostSwitched,
+  kExistingUserNewChromebookAdded,
+};
+
+// Provides an API to the MultiDevice Setup flow. Designed to be exposed
+// primarily to the MultiDevice setup flow at chrome://multidevice-setup (normal
+// usage) as well as the ProximityAuth debug WebUI page at
+// chrome://proximity-auth (debugging only).
+// TODO(khorimoto): Finish fleshing out this interface.
+interface MultiDeviceSetup {
+  // Registers the observer to be used by the service. Once this function has
+  // been called, the service will begin monitoring changes to synced devices
+  // and will notify |observer| when appropriate. Only one observer can be
+  // active at a time; calling this function a second time will remove the
+  // first observer added.
+  SetObserver(MultiDeviceSetupObserver observer) => ();
+
+  // Triggers an event to be dispatched by the service. This API function is
+  // intended to be used only for debugging in the chrome://proximity-auth page.
+  // During normal usage, events are triggered internally within the service.
+  TriggerEventForDebugging(EventTypeForDebugging type) => (bool success);
+};