Make IsolatedDeviceProvider resilient to device service terminations

In the event that the device service is terminated, but the manager
process is still around, attempt to re-create the device service.

In exposing/creating a test for this, the "XRTestHookRegistration"
interface name no longer made sense and thus had to be renamed.

Bug: 912959
Change-Id: I92c2a0f7f7927b7aacbb6a369c5154f720717003
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1531739
Commit-Queue: Alexander Cooper <alcooper@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Bill Orr <billorr@chromium.org>
Reviewed-by: Brian Sheedy <bsheedy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#643641}
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index 058b87f..9cd3011 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -709,6 +709,7 @@
       sources += [
         "webxr_vr_frame_pose_browser_test.cc",
         "webxr_vr_input_browser_test.cc",
+        "webxr_vr_isolated_device_service_test.cc",
         "webxr_vr_permission_request_browser_test.cc",
         "webxr_vr_pixel_browser_test.cc",
         "webxr_vr_tab_browser_test.cc",
diff --git a/chrome/browser/vr/service/isolated_device_provider.cc b/chrome/browser/vr/service/isolated_device_provider.cc
index 8ac2433..81c14d9e 100644
--- a/chrome/browser/vr/service/isolated_device_provider.cc
+++ b/chrome/browser/vr/service/isolated_device_provider.cc
@@ -11,6 +11,10 @@
 #include "device/vr/isolated_gamepad_data_fetcher.h"
 #include "services/service_manager/public/cpp/connector.h"
 
+namespace {
+constexpr int kMaxRetries = 3;
+}
+
 namespace vr {
 
 void IsolatedVRDeviceProvider::Initialize(
@@ -21,22 +25,12 @@
     base::RepeatingCallback<void(device::mojom::XRDeviceId)>
         remove_device_callback,
     base::OnceClosure initialization_complete) {
-  content::ServiceManagerConnection* connection =
-      content::ServiceManagerConnection::GetForProcess();
-  connection->GetConnector()->BindInterface(
-      device::mojom::kVrIsolatedServiceName,
-      mojo::MakeRequest(&device_provider_));
-
-  device_provider_.set_connection_error_handler(base::BindOnce(
-      &IsolatedVRDeviceProvider::OnServerError, base::Unretained(this)));
-
-  device::mojom::IsolatedXRRuntimeProviderClientPtr client;
-  binding_.Bind(mojo::MakeRequest(&client));
-  device_provider_->RequestDevices(std::move(client));
 
   add_device_callback_ = std::move(add_device_callback);
   remove_device_callback_ = std::move(remove_device_callback);
   initialization_complete_ = std::move(initialization_complete);
+
+  SetupDeviceProvider();
 }
 
 bool IsolatedVRDeviceProvider::Initialized() {
@@ -76,16 +70,46 @@
 
   // At this point, XRRuntimeManager may be blocked waiting for us to return
   // that we've enumerated all runtimes/devices.  If we lost the connection to
-  // the service, we won't ever get devices, so report we are done now.
+  // the service, we'll try again.  If we've already tried too many times,
+  // then just assume we won't ever get devices, so report we are done now.
   // This will unblock WebXR/WebVR promises so they can reject indicating we
   // never found devices.
-  if (!initialized_)
+  if (!initialized_ && retry_count_ >= kMaxRetries) {
     OnDevicesEnumerated();
+  } else {
+    device_provider_.reset();
+    binding_.Close();
+    retry_count_++;
+    SetupDeviceProvider();
+  }
 }
 
 void IsolatedVRDeviceProvider::OnDevicesEnumerated() {
-  initialized_ = true;
-  std::move(initialization_complete_).Run();
+  if (!initialized_) {
+    initialized_ = true;
+    std::move(initialization_complete_).Run();
+  }
+
+  // Either we've hit the max retries and given up (in which case we don't have
+  // a device provider which could error out and cause us to retry) or we've
+  // successfully gotten the device provider again after a retry, and we should
+  // reset our count in case it gets disconnected.
+  retry_count_ = 0;
+}
+
+void IsolatedVRDeviceProvider::SetupDeviceProvider() {
+  content::ServiceManagerConnection* connection =
+      content::ServiceManagerConnection::GetForProcess();
+  connection->GetConnector()->BindInterface(
+      device::mojom::kVrIsolatedServiceName,
+      mojo::MakeRequest(&device_provider_));
+
+  device_provider_.set_connection_error_handler(base::BindOnce(
+      &IsolatedVRDeviceProvider::OnServerError, base::Unretained(this)));
+
+  device::mojom::IsolatedXRRuntimeProviderClientPtr client;
+  binding_.Bind(mojo::MakeRequest(&client));
+  device_provider_->RequestDevices(std::move(client));
 }
 
 IsolatedVRDeviceProvider::IsolatedVRDeviceProvider() : binding_(this) {}
diff --git a/chrome/browser/vr/service/isolated_device_provider.h b/chrome/browser/vr/service/isolated_device_provider.h
index c18c8f1e..6182478d 100644
--- a/chrome/browser/vr/service/isolated_device_provider.h
+++ b/chrome/browser/vr/service/isolated_device_provider.h
@@ -44,8 +44,10 @@
   void OnDeviceRemoved(device::mojom::XRDeviceId id) override;
   void OnDevicesEnumerated() override;
   void OnServerError();
+  void SetupDeviceProvider();
 
   bool initialized_ = false;
+  int retry_count_ = 0;
   device::mojom::IsolatedXRRuntimeProviderPtr device_provider_;
 
   base::RepeatingCallback<void(device::mojom::XRDeviceId,
diff --git a/chrome/browser/vr/test/mock_xr_device_hook_base.cc b/chrome/browser/vr/test/mock_xr_device_hook_base.cc
index 87db8cb..65fd3db 100644
--- a/chrome/browser/vr/test/mock_xr_device_hook_base.cc
+++ b/chrome/browser/vr/test/mock_xr_device_hook_base.cc
@@ -56,7 +56,7 @@
       content::ServiceManagerConnection::GetForProcess();
   connection->GetConnector()->BindInterface(
       device::mojom::kVrIsolatedServiceName,
-      mojo::MakeRequest(&test_hook_registration_));
+      mojo::MakeRequest(&service_test_hook_));
 
   device_test::mojom::XRTestHookPtr client;
   binding_.Bind(mojo::MakeRequest(&client));
@@ -65,7 +65,7 @@
   // For now, always have the HMD connected.
   tracked_classes_[0] =
       device_test::mojom::TrackedDeviceClass::kTrackedDeviceHmd;
-  test_hook_registration_->SetTestHook(std::move(client));
+  service_test_hook_->SetTestHook(std::move(client));
 }
 
 MockXRDeviceHookBase::~MockXRDeviceHookBase() {
@@ -73,11 +73,11 @@
 }
 
 void MockXRDeviceHookBase::StopHooking() {
-  // We don't call test_hook_registration_->SetTestHook(nullptr), since that
+  // We don't call service_test_hook_->SetTestHook(nullptr), since that
   // will potentially deadlock with reentrant or crossing synchronous mojo
   // calls.
   binding_.Close();
-  test_hook_registration_ = nullptr;
+  service_test_hook_ = nullptr;
 }
 
 void MockXRDeviceHookBase::OnFrameSubmitted(
@@ -166,6 +166,11 @@
   return device::kMaxTrackedDevices;
 }
 
+void MockXRDeviceHookBase::TerminateDeviceServiceProcessForTesting() {
+  mojo::ScopedAllowSyncCallForTesting scoped_allow_sync;
+  service_test_hook_->TerminateDeviceServiceProcessForTesting();
+}
+
 void MockXRDeviceHookBase::UpdateController(
     unsigned int index,
     const device::ControllerFrameData& updated_data) {
diff --git a/chrome/browser/vr/test/mock_xr_device_hook_base.h b/chrome/browser/vr/test/mock_xr_device_hook_base.h
index 2ec7c05b..47dfd2f 100644
--- a/chrome/browser/vr/test/mock_xr_device_hook_base.h
+++ b/chrome/browser/vr/test/mock_xr_device_hook_base.h
@@ -42,6 +42,7 @@
       override;
 
   // MockXRDeviceHookBase
+  void TerminateDeviceServiceProcessForTesting();
   unsigned int ConnectController(
       const device::ControllerFrameData& initial_data);
   void UpdateController(unsigned int index,
@@ -59,7 +60,7 @@
 
  private:
   mojo::Binding<device_test::mojom::XRTestHook> binding_;
-  device_test::mojom::XRTestHookRegistrationPtr test_hook_registration_;
+  device_test::mojom::XRServiceTestHookPtr service_test_hook_;
 };
 
 #endif  // CHROME_BROWSER_VR_TEST_MOCK_XR_DEVICE_HOOK_BASE_H_
diff --git a/chrome/browser/vr/webxr_vr_isolated_device_service_test.cc b/chrome/browser/vr/webxr_vr_isolated_device_service_test.cc
new file mode 100644
index 0000000..64bad90
--- /dev/null
+++ b/chrome/browser/vr/webxr_vr_isolated_device_service_test.cc
@@ -0,0 +1,35 @@
+// Copyright 2019 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 "chrome/browser/vr/test/webvr_browser_test.h"
+
+#include "build/build_config.h"
+#include "chrome/browser/vr/test/mock_xr_device_hook_base.h"
+#include "chrome/browser/vr/test/webxr_vr_browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+
+namespace vr {
+
+// Tests that we can recover from a crash/disconnect on the DeviceService
+IN_PROC_BROWSER_TEST_F(WebXrVrBrowserTestStandard,
+                       TestDeviceServiceDisconnect) {
+  LoadUrlAndAwaitInitialization(
+      GetFileUrlForHtmlTestFile("test_isolated_device_service_disconnect"));
+
+  // We expect one change from the initial device being available.
+  PollJavaScriptBooleanOrFail("deviceChanges === 1", kPollTimeoutMedium);
+
+  EnterSessionWithUserGestureOrFail();
+  MockXRDeviceHookBase device_hook;
+  device_hook.TerminateDeviceServiceProcessForTesting();
+
+  // We expect one change indicating the device was disconnected, and then
+  // one more indicating that the device was re-connected.
+  PollJavaScriptBooleanOrFail("deviceChanges === 3", kPollTimeoutMedium);
+
+  // One last check now that we have the device change that we can actually
+  // still enter an immersive session.
+  EnterSessionWithUserGestureOrFail();
+}
+}  // namespace vr
diff --git a/chrome/services/isolated_xr_device/BUILD.gn b/chrome/services/isolated_xr_device/BUILD.gn
index 0df09b4f..48ade061 100644
--- a/chrome/services/isolated_xr_device/BUILD.gn
+++ b/chrome/services/isolated_xr_device/BUILD.gn
@@ -8,8 +8,8 @@
     "xr_device_service.h",
     "xr_runtime_provider.cc",
     "xr_runtime_provider.h",
-    "xr_test_hook_registration.cc",
-    "xr_test_hook_registration.h",
+    "xr_service_test_hook.cc",
+    "xr_service_test_hook.h",
     "xr_test_hook_wrapper.cc",
     "xr_test_hook_wrapper.h",
   ]
diff --git a/chrome/services/isolated_xr_device/manifest.cc b/chrome/services/isolated_xr_device/manifest.cc
index 24651e8..473451b 100644
--- a/chrome/services/isolated_xr_device/manifest.cc
+++ b/chrome/services/isolated_xr_device/manifest.cc
@@ -19,7 +19,7 @@
                            .Build())
           .ExposeCapability("xr_device_test_hook",
                             service_manager::Manifest::InterfaceList<
-                                device_test::mojom::XRTestHookRegistration>())
+                                device_test::mojom::XRServiceTestHook>())
           .ExposeCapability("xr_device_provider",
                             service_manager::Manifest::InterfaceList<
                                 device::mojom::IsolatedXRRuntimeProvider>())
diff --git a/chrome/services/isolated_xr_device/xr_device_service.cc b/chrome/services/isolated_xr_device/xr_device_service.cc
index 73a84f6..29dad1a 100644
--- a/chrome/services/isolated_xr_device/xr_device_service.cc
+++ b/chrome/services/isolated_xr_device/xr_device_service.cc
@@ -6,7 +6,7 @@
 
 #include "base/bind.h"
 #include "chrome/services/isolated_xr_device/xr_runtime_provider.h"
-#include "chrome/services/isolated_xr_device/xr_test_hook_registration.h"
+#include "chrome/services/isolated_xr_device/xr_service_test_hook.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 
 namespace device {
@@ -19,9 +19,9 @@
 }
 
 void XrDeviceService::OnTestHookRequest(
-    device_test::mojom::XRTestHookRegistrationRequest request) {
+    device_test::mojom::XRServiceTestHookRequest request) {
   mojo::MakeStrongBinding(
-      std::make_unique<XRTestHookRegistration>(service_keepalive_.CreateRef()),
+      std::make_unique<XRServiceTestHook>(service_keepalive_.CreateRef()),
       std::move(request));
 }
 
diff --git a/chrome/services/isolated_xr_device/xr_device_service.h b/chrome/services/isolated_xr_device/xr_device_service.h
index 25b968a..26f24a1e 100644
--- a/chrome/services/isolated_xr_device/xr_device_service.h
+++ b/chrome/services/isolated_xr_device/xr_device_service.h
@@ -23,8 +23,7 @@
 
   void OnDeviceProviderRequest(
       device::mojom::IsolatedXRRuntimeProviderRequest request);
-  void OnTestHookRequest(
-      device_test::mojom::XRTestHookRegistrationRequest request);
+  void OnTestHookRequest(device_test::mojom::XRServiceTestHookRequest request);
 
  private:
   // service_manager::Service:
diff --git a/chrome/services/isolated_xr_device/xr_test_hook_registration.cc b/chrome/services/isolated_xr_device/xr_service_test_hook.cc
similarity index 77%
rename from chrome/services/isolated_xr_device/xr_test_hook_registration.cc
rename to chrome/services/isolated_xr_device/xr_service_test_hook.cc
index d69f88a..03320cab8 100644
--- a/chrome/services/isolated_xr_device/xr_test_hook_registration.cc
+++ b/chrome/services/isolated_xr_device/xr_service_test_hook.cc
@@ -2,16 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/services/isolated_xr_device/xr_test_hook_registration.h"
+#include "chrome/services/isolated_xr_device/xr_service_test_hook.h"
 #include "base/bind.h"
+#include "base/process/process.h"
 #include "chrome/services/isolated_xr_device/xr_test_hook_wrapper.h"
 #include "device/vr/openvr/openvr_api_wrapper.h"
 
 namespace device {
 
-void XRTestHookRegistration::SetTestHook(
+void XRServiceTestHook::SetTestHook(
     device_test::mojom::XRTestHookPtr hook,
-    device_test::mojom::XRTestHookRegistration::SetTestHookCallback callback) {
+    device_test::mojom::XRServiceTestHook::SetTestHookCallback callback) {
   // Create a new wrapper (or use null)
   std::unique_ptr<XRTestHookWrapper> wrapper =
       hook ? std::make_unique<XRTestHookWrapper>(hook.PassInterface())
@@ -26,7 +27,12 @@
   std::move(callback).Run();
 }
 
-XRTestHookRegistration::~XRTestHookRegistration() {
+void XRServiceTestHook::TerminateDeviceServiceProcessForTesting(
+    DeviceCrashCallback callback) {
+  base::Process::TerminateCurrentProcessImmediately(1);
+}
+
+XRServiceTestHook::~XRServiceTestHook() {
   // If we have an existing wrapper, and it is bound to a thread, post a message
   // to destroy it on that thread.
   if (wrapper_) {
@@ -44,7 +50,7 @@
   }
 }
 
-XRTestHookRegistration::XRTestHookRegistration(
+XRServiceTestHook::XRServiceTestHook(
     std::unique_ptr<service_manager::ServiceKeepaliveRef> service_ref)
     : service_ref_(std::move(service_ref)) {}
 
diff --git a/chrome/services/isolated_xr_device/xr_service_test_hook.h b/chrome/services/isolated_xr_device/xr_service_test_hook.h
new file mode 100644
index 0000000..58a8f375c5
--- /dev/null
+++ b/chrome/services/isolated_xr_device/xr_service_test_hook.h
@@ -0,0 +1,38 @@
+// 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 CHROME_SERVICES_ISOLATED_XR_DEVICE_XR_SERVICE_TEST_HOOK_H_
+#define CHROME_SERVICES_ISOLATED_XR_DEVICE_XR_SERVICE_TEST_HOOK_H_
+
+#include <memory>
+
+#include "device/vr/public/mojom/browser_test_interfaces.mojom.h"
+#include "services/service_manager/public/cpp/service_keepalive.h"
+
+namespace device {
+class XRTestHookWrapper;
+
+class XRServiceTestHook : public device_test::mojom::XRServiceTestHook {
+ public:
+  explicit XRServiceTestHook(
+      std::unique_ptr<service_manager::ServiceKeepaliveRef> service_ref);
+  ~XRServiceTestHook() final;
+
+  using DeviceCrashCallback = device_test::mojom::XRServiceTestHook::
+      TerminateDeviceServiceProcessForTestingCallback;
+  // device_test::mojom::XRServiceTestHook
+  void SetTestHook(device_test::mojom::XRTestHookPtr hook,
+                   device_test::mojom::XRServiceTestHook::SetTestHookCallback
+                       callback) override;
+  void TerminateDeviceServiceProcessForTesting(
+      DeviceCrashCallback callback) override;
+
+ private:
+  std::unique_ptr<XRTestHookWrapper> wrapper_;
+  const std::unique_ptr<service_manager::ServiceKeepaliveRef> service_ref_;
+};
+
+}  // namespace device
+
+#endif  // CHROME_SERVICES_ISOLATED_XR_DEVICE_XR_SERVICE_TEST_HOOK_H_
diff --git a/chrome/services/isolated_xr_device/xr_test_hook_registration.h b/chrome/services/isolated_xr_device/xr_test_hook_registration.h
deleted file mode 100644
index ccb06d6..0000000
--- a/chrome/services/isolated_xr_device/xr_test_hook_registration.h
+++ /dev/null
@@ -1,36 +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.
-
-#ifndef CHROME_SERVICES_ISOLATED_XR_DEVICE_XR_TEST_HOOK_REGISTRATION_H_
-#define CHROME_SERVICES_ISOLATED_XR_DEVICE_XR_TEST_HOOK_REGISTRATION_H_
-
-#include <memory>
-
-#include "device/vr/public/mojom/browser_test_interfaces.mojom.h"
-#include "services/service_manager/public/cpp/service_keepalive.h"
-
-namespace device {
-class XRTestHookWrapper;
-
-class XRTestHookRegistration
-    : public device_test::mojom::XRTestHookRegistration {
- public:
-  explicit XRTestHookRegistration(
-      std::unique_ptr<service_manager::ServiceKeepaliveRef> service_ref);
-  ~XRTestHookRegistration() final;
-
-  // device_test::mojom::XRTestHookRegistration
-  void SetTestHook(
-      device_test::mojom::XRTestHookPtr hook,
-      device_test::mojom::XRTestHookRegistration::SetTestHookCallback callback)
-      override;
-
- private:
-  std::unique_ptr<XRTestHookWrapper> wrapper_;
-  const std::unique_ptr<service_manager::ServiceKeepaliveRef> service_ref_;
-};
-
-}  // namespace device
-
-#endif  // CHROME_SERVICES_ISOLATED_XR_DEVICE_XR_TEST_HOOK_REGISTRATION_H_
diff --git a/chrome/test/data/xr/e2e_test_files/html/test_isolated_device_service_disconnect.html b/chrome/test/data/xr/e2e_test_files/html/test_isolated_device_service_disconnect.html
new file mode 100644
index 0000000..d6b0ce3
--- /dev/null
+++ b/chrome/test/data/xr/e2e_test_files/html/test_isolated_device_service_disconnect.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<!--
+WebXR page without any code specific to one test
+-->
+<html>
+  <head>
+    <link rel="stylesheet" type="text/css" href="../resources/webxr_e2e.css">
+  </head>
+  <body>
+    <canvas id="webgl-canvas"></canvas>
+    <script src="../../../../../../third_party/blink/web_tests/resources/testharness.js"></script>
+    <script src="../resources/webxr_e2e.js"></script>
+    <script src="../resources/webxr_boilerplate.js"></script>
+    <script>
+      var deviceChanges = 0;
+      navigator.xr.ondevicechange = () => { deviceChanges++; };
+    </script>
+  </body>
+</html>
diff --git a/device/vr/openvr/openvr_api_wrapper.cc b/device/vr/openvr/openvr_api_wrapper.cc
index 5f5d463..897a8a2 100644
--- a/device/vr/openvr/openvr_api_wrapper.cc
+++ b/device/vr/openvr/openvr_api_wrapper.cc
@@ -34,8 +34,8 @@
   // maintaining thread safety, typically by not changing the test hook
   // while presenting.
   test_hook_ = hook;
-  if (test_hook_registration_) {
-    test_hook_registration_->SetTestHook(test_hook_);
+  if (service_test_hook_) {
+    service_test_hook_->SetTestHook(test_hook_);
   }
 }
 
@@ -63,15 +63,15 @@
   if (test_hook_) {
     // Allow our mock implementation of OpenVR to be controlled by tests.
     // Note that SetTestHook must be called before CreateDevice, or
-    // test_hook_registration_s will remain null.  This is a good pattern for
+    // service_test_hook_s will remain null.  This is a good pattern for
     // tests anyway, since the alternative is we start mocking part-way through
     // using the device, and end up with race conditions for when we started
     // controlling things.
     vr::EVRInitError eError;
-    test_hook_registration_ = reinterpret_cast<TestHookRegistration*>(
+    service_test_hook_ = static_cast<ServiceTestHook*>(
         vr::VR_GetGenericInterface(kChromeOpenVRTestHookAPI, &eError));
-    if (test_hook_registration_) {
-      test_hook_registration_->SetTestHook(test_hook_);
+    if (service_test_hook_) {
+      service_test_hook_->SetTestHook(test_hook_);
       test_hook_->AttachCurrentThread();
     }
   }
@@ -84,7 +84,7 @@
   initialized_ = false;
   system_ = nullptr;
   compositor_ = nullptr;
-  test_hook_registration_ = nullptr;
+  service_test_hook_ = nullptr;
   current_task_runner_ = nullptr;
   if (test_hook_)
     test_hook_->DetachCurrentThread();
@@ -95,7 +95,7 @@
 
 VRTestHook* OpenVRWrapper::test_hook_ = nullptr;
 bool OpenVRWrapper::any_initialized_ = false;
-TestHookRegistration* OpenVRWrapper::test_hook_registration_ = nullptr;
+ServiceTestHook* OpenVRWrapper::service_test_hook_ = nullptr;
 
 std::string GetOpenVRString(vr::IVRSystem* vr_system,
                             vr::TrackedDeviceProperty prop) {
@@ -113,4 +113,4 @@
   return out;
 }
 
-}  // namespace device
\ No newline at end of file
+}  // namespace device
diff --git a/device/vr/openvr/openvr_api_wrapper.h b/device/vr/openvr/openvr_api_wrapper.h
index ba80852..a1b11fd 100644
--- a/device/vr/openvr/openvr_api_wrapper.h
+++ b/device/vr/openvr/openvr_api_wrapper.h
@@ -15,7 +15,7 @@
 
 namespace device {
 class VRTestHook;
-class TestHookRegistration;
+class ServiceTestHook;
 
 class OpenVRWrapper {
  public:
@@ -40,7 +40,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> current_task_runner_;
   bool initialized_ = false;
 
-  static TestHookRegistration* test_hook_registration_;
+  static ServiceTestHook* service_test_hook_;
   static VRTestHook* test_hook_;
   static bool any_initialized_;
 };
@@ -50,4 +50,4 @@
 
 }  // namespace device
 
-#endif  // DEVICE_VR_OPENVR_OPENVR_API_WRAPPER_H_
\ No newline at end of file
+#endif  // DEVICE_VR_OPENVR_OPENVR_API_WRAPPER_H_
diff --git a/device/vr/openvr/test/fake_openvr_impl_api.cc b/device/vr/openvr/test/fake_openvr_impl_api.cc
index 13bfdf7..8f81197 100644
--- a/device/vr/openvr/test/fake_openvr_impl_api.cc
+++ b/device/vr/openvr/test/fake_openvr_impl_api.cc
@@ -407,7 +407,7 @@
   if (strcmp(name_and_version, IVRCompositor_Version) == 0)
     return static_cast<IVRCompositor*>(&g_compositor);
   if (strcmp(name_and_version, device::kChromeOpenVRTestHookAPI) == 0)
-    return static_cast<device::TestHookRegistration*>(&g_test_helper);
+    return static_cast<device::ServiceTestHook*>(&g_test_helper);
 
   *error = VRInitError_Init_InvalidInterface;
   return nullptr;
diff --git a/device/vr/openvr/test/test_helper.h b/device/vr/openvr/test/test_helper.h
index 0cad4de..d7c278bb 100644
--- a/device/vr/openvr/test/test_helper.h
+++ b/device/vr/openvr/test/test_helper.h
@@ -18,7 +18,7 @@
   float projection[4];
 };
 
-class TestHelper : public device::TestHookRegistration {
+class TestHelper : public device::ServiceTestHook {
  public:
   // Methods called by mock OpenVR APIs.
   void OnPresentedFrame(ID3D11Texture2D* texture,
@@ -47,7 +47,7 @@
   void AttachToCurrentThread();
   void DetachFromCurrentThread();
 
-  // TestHookRegistration
+  // ServiceTestHook
   void SetTestHook(device::VRTestHook* hook) final;
 
  private:
@@ -57,4 +57,4 @@
 
 }  // namespace vr
 
-#endif  // DEVICE_VR_OPENVR_TEST_TEST_HELPER_H_
\ No newline at end of file
+#endif  // DEVICE_VR_OPENVR_TEST_TEST_HELPER_H_
diff --git a/device/vr/public/mojom/browser_test_interfaces.mojom b/device/vr/public/mojom/browser_test_interfaces.mojom
index bb2122b..ad2a2f5d 100644
--- a/device/vr/public/mojom/browser_test_interfaces.mojom
+++ b/device/vr/public/mojom/browser_test_interfaces.mojom
@@ -121,6 +121,11 @@
 // Interface exposed by IsolatedXRService to allow browser tests to hook VR APIs
 // It is always hosted in the XRDevice process, but only has effects while
 // running in browser tests with mock implementations of runtimes.
-interface XRTestHookRegistration {
+interface XRServiceTestHook {
   [Sync] SetTestHook(XRTestHook? hook) => ();
+
+  // Called by tests to trigger a termination of the Device Service Process
+  // To test that the product can properly handle the service either crashing
+  // or (more expectedly) being terminated by some other running process.
+  [Sync] TerminateDeviceServiceProcessForTesting() => ();
 };
diff --git a/device/vr/test/test_hook.h b/device/vr/test/test_hook.h
index b6e6c23d..08decb9c 100644
--- a/device/vr/test/test_hook.h
+++ b/device/vr/test/test_hook.h
@@ -96,11 +96,11 @@
   virtual void DetachCurrentThread() = 0;
 };
 
-class TestHookRegistration {
+class ServiceTestHook {
  public:
   virtual void SetTestHook(VRTestHook*) = 0;
 };
 
 }  // namespace device
 
-#endif  // DEVICE_VR_TEST_TEST_HOOK_H_
\ No newline at end of file
+#endif  // DEVICE_VR_TEST_TEST_HOOK_H_