Pass onVsync() callbacks from vr flinger back to surface flinger

When vr flinger is active we weren't returning onVsync() callbacks to
surface flinger from vr_hwc, which caused surface flinger frame
scheduling to drift with respect to display vsync, causing system
latency and performance to be less predictable than expected. Since
actual display vsync periods are usually slightly different than the
reported vsync period from hardware composer, the lack of vsync feedback
would also cause surface flinger to occasionally either miss a frame or
produce an extra frame during a vsync period.

This CL adds a new vsync service in vr flinger. Vr_hwc registers to
receive vsync callbacks from that service, and it forwards the vsync
events on to surface flinger. I confirmed using systrace this fixes the
scheduling drift in surface flinger.

I also removed the old PDX vsync service, which exposed obsolete
information and is no longer used anywhere.

The DispSync code in surface flinger needed to be updated as
well. DispSync uses the hardware composer present fence timestamps to
make vsync predictions, and when the present fence-based prediction
is close enough to the actual vsync times, it turns off the vysnc
callbacks. However the present fences returned from vr_hwc are not
correlated in any way to vsync times, so I changed the code to ignore
present fences when vr flinger is active.

Bug: 72890037

Test: - Used systrace to confirm surface flinger scheduling no longer
drifts with respect to the real vsync.

- Added new traces to confirm the vsync callbacks don't take a lot of
  cpu time.

- Confirmed that hardware vsync events in surface flinger are turned off
  when DispSync's predictions align with the present fence timestamp, as
  was previously the case.

- Confirmed hardware vsync events in surface flinger are turned off when
  the display is turned off.

- Confirmed that hardware vsync events are turned off as normal even
  when the zero phase tracer is turned on in DispSync.cpp.

- Confirmed that when we enter vr flinger, we turn usage of the present
  fence off in DispSync, and when we exit vr flinger, we turn usage of
  the present fence back on.

- Confirmed that I can't bind to the new vsync service from a normal
  Android application, and system processes (other than vr_hwc) are
  prevented from connecting by selinux.

- All tests mentioned above were done on a Pixel 2 (non-XL).

Change-Id: Ie009040e125f4d31958a1575b2e2bbe3e601a0f4
diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp
index 9c67881..8c354fb 100644
--- a/libs/vr/libdisplay/Android.bp
+++ b/libs/vr/libdisplay/Android.bp
@@ -16,8 +16,8 @@
     "display_client.cpp",
     "display_manager_client.cpp",
     "display_protocol.cpp",
-    "vsync_client.cpp",
     "shared_buffer_helpers.cpp",
+    "vsync_service.cpp",
 ]
 
 localIncludeFiles = [
diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_client.h b/libs/vr/libdisplay/include/private/dvr/vsync_client.h
deleted file mode 100644
index 1eeb80e..0000000
--- a/libs/vr/libdisplay/include/private/dvr/vsync_client.h
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef ANDROID_DVR_VSYNC_CLIENT_H_
-#define ANDROID_DVR_VSYNC_CLIENT_H_
-
-#include <stdint.h>
-
-#include <pdx/client.h>
-
-struct dvr_vsync_client {};
-
-namespace android {
-namespace dvr {
-
-/*
- * VSyncClient is a remote interface to the vsync service in displayd.
- * This class is used to wait for and retrieve information about the
- * display vsync.
- */
-class VSyncClient : public pdx::ClientBase<VSyncClient>,
-                    public dvr_vsync_client {
- public:
-  /*
-   * Wait for the next vsync signal.
-   * The timestamp (in ns) is written into *ts when ts is non-NULL.
-   */
-  int Wait(int64_t* timestamp_ns);
-
-  /*
-   * Returns the file descriptor used to communicate with the vsync system
-   * service or -1 on error.
-   */
-  int GetFd();
-
-  /*
-   * Clears the select/poll/epoll event so that subsequent calls to
-   * these will not signal until the next vsync.
-   */
-  int Acknowledge();
-
-  /*
-   * Get the timestamp of the last vsync event in ns. This call has
-   * the same side effect on events as Acknowledge(), which saves
-   * an IPC message.
-   */
-  int GetLastTimestamp(int64_t* timestamp_ns);
-
-  /*
-   * Get vsync scheduling info.
-   * Get the estimated timestamp of the next GPU lens warp preemption event in
-   * ns. Also returns the corresponding vsync count that the next lens warp
-   * operation will target. This call has the same side effect on events as
-   * Acknowledge(), which saves an IPC message.
-   */
-  int GetSchedInfo(int64_t* vsync_period_ns, int64_t* next_timestamp_ns,
-                   uint32_t* next_vsync_count);
-
- private:
-  friend BASE;
-
-  VSyncClient();
-  explicit VSyncClient(long timeout_ms);
-
-  VSyncClient(const VSyncClient&) = delete;
-  void operator=(const VSyncClient&) = delete;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_VSYNC_CLIENT_H_
diff --git a/libs/vr/libdisplay/include/private/dvr/vsync_service.h b/libs/vr/libdisplay/include/private/dvr/vsync_service.h
new file mode 100644
index 0000000..152464a
--- /dev/null
+++ b/libs/vr/libdisplay/include/private/dvr/vsync_service.h
@@ -0,0 +1,65 @@
+#ifndef ANDROID_DVR_VSYNC_SERVICE_H_
+#define ANDROID_DVR_VSYNC_SERVICE_H_
+
+#include <binder/IInterface.h>
+
+namespace android {
+namespace dvr {
+
+class IVsyncCallback : public IInterface {
+ public:
+  DECLARE_META_INTERFACE(VsyncCallback)
+
+  enum {
+    ON_VSYNC = IBinder::FIRST_CALL_TRANSACTION
+  };
+
+  virtual status_t onVsync(int64_t vsync_timestamp) = 0;
+};
+
+class BnVsyncCallback : public BnInterface<IVsyncCallback> {
+ public:
+  virtual status_t onTransact(uint32_t code, const Parcel& data,
+                              Parcel* reply, uint32_t flags = 0);
+};
+
+// Register a callback with IVsyncService to be notified of vsync events and
+// timestamps. There's also a shared memory vsync buffer defined in
+// dvr_shared_buffers.h. IVsyncService has advantages over the vsync shared
+// memory buffer that make it preferable in certain situations:
+//
+// 1. The shared memory buffer lifetime is controlled by VrCore. IVsyncService
+// is always available as long as surface flinger is running.
+//
+// 2. IVsyncService will make a binder callback when a vsync event occurs. This
+// allows the client to not write code to implement periodic "get the latest
+// vsync" calls, which is necessary with the vsync shared memory buffer.
+//
+// 3. The IVsyncService provides the real vsync timestamp reported by hardware
+// composer, whereas the vsync shared memory buffer only has predicted vsync
+// times.
+class IVsyncService : public IInterface {
+public:
+  DECLARE_META_INTERFACE(VsyncService)
+
+  static const char* GetServiceName() { return "vrflinger_vsync"; }
+
+  enum {
+    REGISTER_CALLBACK = IBinder::FIRST_CALL_TRANSACTION,
+    UNREGISTER_CALLBACK
+  };
+
+  virtual status_t registerCallback(const sp<IVsyncCallback> callback) = 0;
+  virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) = 0;
+};
+
+class BnVsyncService : public BnInterface<IVsyncService> {
+ public:
+  virtual status_t onTransact(uint32_t code, const Parcel& data,
+                              Parcel* reply, uint32_t flags = 0);
+};
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_VSYNC_SERVICE_H_
diff --git a/libs/vr/libdisplay/vsync_client.cpp b/libs/vr/libdisplay/vsync_client.cpp
deleted file mode 100644
index bc6cf6c..0000000
--- a/libs/vr/libdisplay/vsync_client.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-#include "include/private/dvr/vsync_client.h"
-
-#include <log/log.h>
-
-#include <pdx/default_transport/client_channel_factory.h>
-#include <private/dvr/display_protocol.h>
-
-using android::dvr::display::VSyncProtocol;
-using android::pdx::Transaction;
-
-namespace android {
-namespace dvr {
-
-VSyncClient::VSyncClient(long timeout_ms)
-    : BASE(pdx::default_transport::ClientChannelFactory::Create(
-               VSyncProtocol::kClientPath),
-           timeout_ms) {}
-
-VSyncClient::VSyncClient()
-    : BASE(pdx::default_transport::ClientChannelFactory::Create(
-          VSyncProtocol::kClientPath)) {}
-
-int VSyncClient::Wait(int64_t* timestamp_ns) {
-  auto status = InvokeRemoteMethod<VSyncProtocol::Wait>();
-  if (!status) {
-    ALOGE("VSyncClient::Wait: Failed to wait for vsync: %s",
-          status.GetErrorMessage().c_str());
-    return -status.error();
-  }
-
-  if (timestamp_ns != nullptr) {
-    *timestamp_ns = status.get();
-  }
-  return 0;
-}
-
-int VSyncClient::GetFd() { return event_fd(); }
-
-int VSyncClient::GetLastTimestamp(int64_t* timestamp_ns) {
-  auto status = InvokeRemoteMethod<VSyncProtocol::GetLastTimestamp>();
-  if (!status) {
-    ALOGE("VSyncClient::GetLastTimestamp: Failed to get vsync timestamp: %s",
-          status.GetErrorMessage().c_str());
-    return -status.error();
-  }
-  *timestamp_ns = status.get();
-  return 0;
-}
-
-int VSyncClient::GetSchedInfo(int64_t* vsync_period_ns, int64_t* timestamp_ns,
-                              uint32_t* next_vsync_count) {
-  if (!vsync_period_ns || !timestamp_ns || !next_vsync_count)
-    return -EINVAL;
-
-  auto status = InvokeRemoteMethod<VSyncProtocol::GetSchedInfo>();
-  if (!status) {
-    ALOGE("VSyncClient::GetSchedInfo:: Failed to get warp timestamp: %s",
-          status.GetErrorMessage().c_str());
-    return -status.error();
-  }
-
-  *vsync_period_ns = status.get().vsync_period_ns;
-  *timestamp_ns = status.get().timestamp_ns;
-  *next_vsync_count = status.get().next_vsync_count;
-  return 0;
-}
-
-int VSyncClient::Acknowledge() {
-  auto status = InvokeRemoteMethod<VSyncProtocol::Acknowledge>();
-  ALOGE_IF(!status, "VSuncClient::Acknowledge: Failed to ack vsync because: %s",
-           status.GetErrorMessage().c_str());
-  return ReturnStatusOrError(status);
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libdisplay/vsync_service.cpp b/libs/vr/libdisplay/vsync_service.cpp
new file mode 100644
index 0000000..43b1196
--- /dev/null
+++ b/libs/vr/libdisplay/vsync_service.cpp
@@ -0,0 +1,146 @@
+#include "include/private/dvr/vsync_service.h"
+
+#include <binder/Parcel.h>
+#include <log/log.h>
+
+namespace android {
+namespace dvr {
+
+status_t BnVsyncCallback::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+  switch (code) {
+    case ON_VSYNC: {
+      CHECK_INTERFACE(IVsyncCallback, data, reply);
+      int64_t vsync_timestamp = 0;
+      status_t result = data.readInt64(&vsync_timestamp);
+      if (result != NO_ERROR) {
+        ALOGE("onVsync failed to readInt64: %d", result);
+        return result;
+      }
+      onVsync(vsync_timestamp);
+      return NO_ERROR;
+    }
+    default: {
+      return BBinder::onTransact(code, data, reply, flags);
+    }
+  }
+}
+
+class BpVsyncCallback : public BpInterface<IVsyncCallback> {
+public:
+  explicit BpVsyncCallback(const sp<IBinder>& impl)
+      : BpInterface<IVsyncCallback>(impl) {}
+  virtual ~BpVsyncCallback() {}
+
+  virtual status_t onVsync(int64_t vsync_timestamp) {
+    Parcel data, reply;
+    status_t result = data.writeInterfaceToken(
+        IVsyncCallback::getInterfaceDescriptor());
+    if (result != NO_ERROR) {
+      ALOGE("onVsync failed to writeInterfaceToken: %d", result);
+      return result;
+    }
+    result = data.writeInt64(vsync_timestamp);
+    if (result != NO_ERROR) {
+      ALOGE("onVsync failed to writeInt64: %d", result);
+      return result;
+    }
+    result = remote()->transact(
+        BnVsyncCallback::ON_VSYNC, data, &reply, TF_ONE_WAY);
+    if (result != NO_ERROR) {
+      ALOGE("onVsync failed to transact: %d", result);
+      return result;
+    }
+    return result;
+  }
+};
+
+IMPLEMENT_META_INTERFACE(VsyncCallback, "android.dvr.IVsyncCallback");
+
+
+status_t BnVsyncService::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+  switch (code) {
+    case REGISTER_CALLBACK: {
+      CHECK_INTERFACE(IVsyncService, data, reply);
+      sp<IBinder> callback;
+      status_t result = data.readStrongBinder(&callback);
+      if (result != NO_ERROR) {
+        ALOGE("registerCallback failed to readStrongBinder: %d", result);
+        return result;
+      }
+      registerCallback(interface_cast<IVsyncCallback>(callback));
+      return NO_ERROR;
+    }
+    case UNREGISTER_CALLBACK: {
+      CHECK_INTERFACE(IVsyncService, data, reply);
+      sp<IBinder> callback;
+      status_t result = data.readStrongBinder(&callback);
+      if (result != NO_ERROR) {
+        ALOGE("unregisterCallback failed to readStrongBinder: %d", result);
+        return result;
+      }
+      unregisterCallback(interface_cast<IVsyncCallback>(callback));
+      return NO_ERROR;
+    }
+    default: {
+      return BBinder::onTransact(code, data, reply, flags);
+    }
+  }
+}
+
+class BpVsyncService : public BpInterface<IVsyncService> {
+public:
+  explicit BpVsyncService(const sp<IBinder>& impl)
+      : BpInterface<IVsyncService>(impl) {}
+  virtual ~BpVsyncService() {}
+
+  virtual status_t registerCallback(const sp<IVsyncCallback> callback) {
+    Parcel data, reply;
+    status_t result = data.writeInterfaceToken(
+        IVsyncService::getInterfaceDescriptor());
+    if (result != NO_ERROR) {
+      ALOGE("registerCallback failed to writeInterfaceToken: %d", result);
+      return result;
+    }
+    result = data.writeStrongBinder(IInterface::asBinder(callback));
+    if (result != NO_ERROR) {
+      ALOGE("registerCallback failed to writeStrongBinder: %d", result);
+      return result;
+    }
+    result = remote()->transact(
+        BnVsyncService::REGISTER_CALLBACK, data, &reply);
+    if (result != NO_ERROR) {
+      ALOGE("registerCallback failed to transact: %d", result);
+      return result;
+    }
+    return result;
+  }
+
+  virtual status_t unregisterCallback(const sp<IVsyncCallback> callback) {
+    Parcel data, reply;
+    status_t result = data.writeInterfaceToken(
+        IVsyncService::getInterfaceDescriptor());
+    if (result != NO_ERROR) {
+      ALOGE("unregisterCallback failed to writeInterfaceToken: %d", result);
+      return result;
+    }
+    result = data.writeStrongBinder(IInterface::asBinder(callback));
+    if (result != NO_ERROR) {
+      ALOGE("unregisterCallback failed to writeStrongBinder: %d", result);
+      return result;
+    }
+    result = remote()->transact(
+        BnVsyncService::UNREGISTER_CALLBACK, data, &reply);
+    if (result != NO_ERROR) {
+      ALOGE("unregisterCallback failed to transact: %d", result);
+      return result;
+    }
+    return result;
+  }
+};
+
+IMPLEMENT_META_INTERFACE(VsyncService, "android.dvr.IVsyncService");
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 00e4d4e..3829951 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -44,7 +44,6 @@
     "dvr_pose.cpp",
     "dvr_surface.cpp",
     "dvr_tracking.cpp",
-    "dvr_vsync.cpp",
 ]
 
 static_libs = [
diff --git a/libs/vr/libdvr/dvr_vsync.cpp b/libs/vr/libdvr/dvr_vsync.cpp
deleted file mode 100644
index 099240e..0000000
--- a/libs/vr/libdvr/dvr_vsync.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "include/dvr/dvr_vsync.h"
-
-#include <utils/Log.h>
-
-#include <private/dvr/vsync_client.h>
-
-extern "C" {
-
-struct DvrVSyncClient {
-  std::unique_ptr<android::dvr::VSyncClient> client;
-};
-
-int dvrVSyncClientCreate(DvrVSyncClient** client_out) {
-  auto client = android::dvr::VSyncClient::Create();
-  if (!client) {
-    ALOGE("dvrVSyncClientCreate: Failed to create vsync client!");
-    return -EIO;
-  }
-
-  *client_out = new DvrVSyncClient{std::move(client)};
-  return 0;
-}
-
-void dvrVSyncClientDestroy(DvrVSyncClient* client) { delete client; }
-
-int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns,
-                               int64_t* next_timestamp_ns,
-                               uint32_t* next_vsync_count) {
-  return client->client->GetSchedInfo(vsync_period_ns, next_timestamp_ns,
-                                      next_vsync_count);
-}
-
-}  // extern "C"
diff --git a/libs/vr/libdvr/include/dvr/dvr_api_entries.h b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
index 6620a37..3006b61 100644
--- a/libs/vr/libdvr/include/dvr/dvr_api_entries.h
+++ b/libs/vr/libdvr/include/dvr/dvr_api_entries.h
@@ -85,9 +85,9 @@
 DVR_V1_API_ENTRY(ReadBufferQueueHandleEvents);
 
 // V-Sync client
-DVR_V1_API_ENTRY(VSyncClientCreate);
-DVR_V1_API_ENTRY(VSyncClientDestroy);
-DVR_V1_API_ENTRY(VSyncClientGetSchedInfo);
+DVR_V1_API_ENTRY_DEPRECATED(VSyncClientCreate);
+DVR_V1_API_ENTRY_DEPRECATED(VSyncClientDestroy);
+DVR_V1_API_ENTRY_DEPRECATED(VSyncClientGetSchedInfo);
 
 // Display surface
 DVR_V1_API_ENTRY(SurfaceCreate);
diff --git a/libs/vr/libdvr/include/dvr/dvr_deleter.h b/libs/vr/libdvr/include/dvr/dvr_deleter.h
index 943384f..fe59d1f 100644
--- a/libs/vr/libdvr/include/dvr/dvr_deleter.h
+++ b/libs/vr/libdvr/include/dvr/dvr_deleter.h
@@ -20,7 +20,6 @@
 typedef struct DvrSurface DvrSurface;
 typedef struct DvrHwcClient DvrHwcClient;
 typedef struct DvrHwcFrame DvrHwcFrame;
-typedef struct DvrVSyncClient DvrVSyncClient;
 
 void dvrBufferDestroy(DvrBuffer* buffer);
 void dvrReadBufferDestroy(DvrReadBuffer* read_buffer);
@@ -32,7 +31,6 @@
 void dvrSurfaceDestroy(DvrSurface* surface);
 void dvrHwcClientDestroy(DvrHwcClient* client);
 void dvrHwcFrameDestroy(DvrHwcFrame* frame);
-void dvrVSyncClientDestroy(DvrVSyncClient* client);
 
 __END_DECLS
 
@@ -55,7 +53,6 @@
   void operator()(DvrSurface* p) { dvrSurfaceDestroy(p); }
   void operator()(DvrHwcClient* p) { dvrHwcClientDestroy(p); }
   void operator()(DvrHwcFrame* p) { dvrHwcFrameDestroy(p); }
-  void operator()(DvrVSyncClient* p) { dvrVSyncClientDestroy(p); }
 };
 
 // Helper to define unique pointers for DVR object types.
@@ -73,7 +70,6 @@
 using UniqueDvrSurface = MakeUniqueDvrPointer<DvrSurface>;
 using UniqueDvrHwcClient = MakeUniqueDvrPointer<DvrHwcClient>;
 using UniqueDvrHwcFrame = MakeUniqueDvrPointer<DvrHwcFrame>;
-using UniqueDvrVSyncClient = MakeUniqueDvrPointer<DvrVSyncClient>;
 
 // TODO(eieio): Add an adapter for std::shared_ptr that injects the deleter into
 // the relevant constructors.
diff --git a/libs/vr/libdvr/include/dvr/dvr_vsync.h b/libs/vr/libdvr/include/dvr/dvr_vsync.h
index 87fdf31..498bb5c 100644
--- a/libs/vr/libdvr/include/dvr/dvr_vsync.h
+++ b/libs/vr/libdvr/include/dvr/dvr_vsync.h
@@ -6,8 +6,6 @@
 
 __BEGIN_DECLS
 
-typedef struct DvrVSyncClient DvrVSyncClient;
-
 // Represents a vsync sample. The size of this struct is 32 bytes.
 typedef struct __attribute__((packed, aligned(16))) DvrVsync {
   // The timestamp for the last vsync in nanoseconds.
@@ -29,19 +27,6 @@
   uint8_t padding[8];
 } DvrVsync;
 
-// Creates a new client to the system vsync service.
-int dvrVSyncClientCreate(DvrVSyncClient** client_out);
-
-// Destroys the vsync client.
-void dvrVSyncClientDestroy(DvrVSyncClient* client);
-
-// Get the estimated timestamp of the next GPU lens warp preemption event in/
-// ns. Also returns the corresponding vsync count that the next lens warp
-// operation will target.
-int dvrVSyncClientGetSchedInfo(DvrVSyncClient* client, int64_t* vsync_period_ns,
-                               int64_t* next_timestamp_ns,
-                               uint32_t* next_vsync_count);
-
 __END_DECLS
 
 #endif  // ANDROID_DVR_VSYNC_H_
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 6f3f1d6..776dd8e 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -20,7 +20,6 @@
     "display_surface.cpp",
     "hardware_composer.cpp",
     "vr_flinger.cpp",
-    "vsync_service.cpp",
 ]
 
 includeFiles = [ "include" ]
diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h
index 3090bd1..6fad58e 100644
--- a/libs/vr/libvrflinger/display_service.h
+++ b/libs/vr/libvrflinger/display_service.h
@@ -60,11 +60,6 @@
   void SetDisplayConfigurationUpdateNotifier(
       DisplayConfigurationUpdateNotifier notifier);
 
-  using VSyncCallback = HardwareComposer::VSyncCallback;
-  void SetVSyncCallback(VSyncCallback callback) {
-    hardware_composer_.SetVSyncCallback(callback);
-  }
-
   void GrantDisplayOwnership() { hardware_composer_.Enable(); }
   void SeizeDisplayOwnership() { hardware_composer_.Disable(); }
   void OnBootFinished() { hardware_composer_.OnBootFinished(); }
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index 44ce78c..e98d592 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -1,5 +1,6 @@
 #include "hardware_composer.h"
 
+#include <binder/IServiceManager.h>
 #include <cutils/properties.h>
 #include <cutils/sched_policy.h>
 #include <fcntl.h>
@@ -52,6 +53,10 @@
 
 const char kUseExternalDisplayProperty[] = "persist.vr.use_external_display";
 
+// Surface flinger uses "VSYNC-sf" and "VSYNC-app" for its version of these
+// events. Name ours similarly.
+const char kVsyncTraceEventName[] = "VSYNC-vrflinger";
+
 // How long to wait after boot finishes before we turn the display off.
 constexpr int kBootFinishedDisplayOffTimeoutSec = 10;
 
@@ -131,6 +136,7 @@
   UpdatePostThreadState(PostThreadState::Quit, true);
   if (post_thread_.joinable())
     post_thread_.join();
+  composer_callback_->SetVsyncService(nullptr);
 }
 
 bool HardwareComposer::Initialize(
@@ -147,6 +153,13 @@
 
   primary_display_ = GetDisplayParams(composer, primary_display_id, true);
 
+  vsync_service_ = new VsyncService;
+  sp<IServiceManager> sm(defaultServiceManager());
+  auto result = sm->addService(String16(VsyncService::GetServiceName()),
+      vsync_service_, false);
+  LOG_ALWAYS_FATAL_IF(result != android::OK,
+      "addService(%s) failed", VsyncService::GetServiceName());
+
   post_thread_event_fd_.Reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
   LOG_ALWAYS_FATAL_IF(
       !post_thread_event_fd_,
@@ -223,6 +236,7 @@
   LOG_ALWAYS_FATAL_IF(!composer_callback_->GotFirstHotplug(),
       "Registered composer callback but didn't get hotplug for primary"
       " display");
+  composer_callback_->SetVsyncService(vsync_service_);
 }
 
 void HardwareComposer::OnPostThreadResumed() {
@@ -242,7 +256,10 @@
   // Standalones only create the composer client once and then use SetPowerMode
   // to control the screen on pause/resume.
   if (!is_standalone_device_) {
-    composer_callback_ = nullptr;
+    if (composer_callback_ != nullptr) {
+      composer_callback_->SetVsyncService(nullptr);
+      composer_callback_ = nullptr;
+    }
     composer_.reset(nullptr);
   } else {
     EnableDisplay(*target_display_, false);
@@ -336,7 +353,6 @@
   // According to the documentation, this fence is signaled at the time of
   // vsync/DMA for physical displays.
   if (error == HWC::Error::None) {
-    ATRACE_INT("HardwareComposer: VsyncFence", present_fence);
     retire_fence_fds_.emplace_back(present_fence);
   } else {
     ATRACE_INT("HardwareComposer: PresentResult", error);
@@ -775,6 +791,11 @@
       std::unique_lock<std::mutex> lock(post_thread_mutex_);
       ALOGI("HardwareComposer::PostThread: Entering quiescent state.");
 
+      if (was_running) {
+        vsync_trace_parity_ = false;
+        ATRACE_INT(kVsyncTraceEventName, 0);
+      }
+
       // Tear down resources.
       OnPostThreadPaused();
       was_running = false;
@@ -848,6 +869,9 @@
       vsync_timestamp = status.get();
     }
 
+    vsync_trace_parity_ = !vsync_trace_parity_;
+    ATRACE_INT(kVsyncTraceEventName, vsync_trace_parity_ ? 1 : 0);
+
     // Advance the vsync counter only if the system is keeping up with hardware
     // vsync to give clients an indication of the delays.
     if (vsync_prediction_interval_ == 1)
@@ -867,11 +891,6 @@
       vsync_ring_->Publish(vsync);
     }
 
-    // Signal all of the vsync clients. Because absolute time is used for the
-    // wakeup time below, this can take a little time if necessary.
-    if (vsync_callback_)
-      vsync_callback_(vsync_timestamp, /*frame_time_estimate*/ 0, vsync_count_);
-
     {
       // Sleep until shortly before vsync.
       ATRACE_NAME("sleep");
@@ -1063,8 +1082,45 @@
            layers_.size());
 }
 
-void HardwareComposer::SetVSyncCallback(VSyncCallback callback) {
-  vsync_callback_ = callback;
+std::vector<sp<IVsyncCallback>>::const_iterator
+HardwareComposer::VsyncService::FindCallback(
+    const sp<IVsyncCallback>& callback) const {
+  sp<IBinder> binder = IInterface::asBinder(callback);
+  return std::find_if(callbacks_.cbegin(), callbacks_.cend(),
+                      [&](const sp<IVsyncCallback>& callback) {
+                        return IInterface::asBinder(callback) == binder;
+                      });
+}
+
+status_t HardwareComposer::VsyncService::registerCallback(
+    const sp<IVsyncCallback> callback) {
+  std::lock_guard<std::mutex> autolock(mutex_);
+  if (FindCallback(callback) == callbacks_.cend()) {
+    callbacks_.push_back(callback);
+  }
+  return NO_ERROR;
+}
+
+status_t HardwareComposer::VsyncService::unregisterCallback(
+    const sp<IVsyncCallback> callback) {
+  std::lock_guard<std::mutex> autolock(mutex_);
+  auto iter = FindCallback(callback);
+  if (iter != callbacks_.cend()) {
+    callbacks_.erase(iter);
+  }
+  return NO_ERROR;
+}
+
+void HardwareComposer::VsyncService::OnVsync(int64_t vsync_timestamp) {
+  ATRACE_NAME("VsyncService::OnVsync");
+  std::lock_guard<std::mutex> autolock(mutex_);
+  for (auto iter = callbacks_.begin(); iter != callbacks_.end();) {
+    if ((*iter)->onVsync(vsync_timestamp) == android::DEAD_OBJECT) {
+      iter = callbacks_.erase(iter);
+    } else {
+      ++iter;
+    }
+  }
 }
 
 Return<void> HardwareComposer::ComposerCallback::onHotplug(
@@ -1123,16 +1179,26 @@
 
 Return<void> HardwareComposer::ComposerCallback::onVsync(Hwc2::Display display,
                                                          int64_t timestamp) {
+  TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|",
+               display, timestamp);
+  std::lock_guard<std::mutex> lock(mutex_);
   DisplayInfo* display_info = GetDisplayInfo(display);
   if (display_info) {
-    TRACE_FORMAT("vsync_callback|display=%" PRIu64 ";timestamp=%" PRId64 "|",
-                 display, timestamp);
     display_info->callback_vsync_timestamp = timestamp;
   }
+  if (primary_display_.id == display && vsync_service_ != nullptr) {
+    vsync_service_->OnVsync(timestamp);
+  }
 
   return Void();
 }
 
+void HardwareComposer::ComposerCallback::SetVsyncService(
+    const sp<VsyncService>& vsync_service) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  vsync_service_ = vsync_service;
+}
+
 HardwareComposer::ComposerCallback::Displays
 HardwareComposer::ComposerCallback::GetDisplays() {
   std::lock_guard<std::mutex> lock(mutex_);
@@ -1149,6 +1215,7 @@
 
 Status<int64_t> HardwareComposer::ComposerCallback::GetVsyncTime(
     hwc2_display_t display) {
+  std::lock_guard<std::mutex> autolock(mutex_);
   DisplayInfo* display_info = GetDisplayInfo(display);
   if (!display_info) {
     ALOGW("Attempt to get vsync time for unknown display %" PRIu64, display);
@@ -1160,7 +1227,6 @@
   if (!event_fd) {
     // Fall back to returning the last timestamp returned by the vsync
     // callback.
-    std::lock_guard<std::mutex> autolock(mutex_);
     return display_info->callback_vsync_timestamp;
   }
 
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index 1d8d463..80fa7ac 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -24,6 +24,7 @@
 #include <pdx/rpc/variant.h>
 #include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/shared_buffer_helpers.h>
+#include <private/dvr/vsync_service.h>
 
 #include "acquired_buffer.h"
 #include "display_surface.h"
@@ -300,8 +301,6 @@
 // will access the state and whether it needs to be synchronized.
 class HardwareComposer {
  public:
-  // Type for vsync callback.
-  using VSyncCallback = std::function<void(int64_t, int64_t, uint32_t)>;
   using RequestDisplayCallback = std::function<void(bool)>;
 
   HardwareComposer();
@@ -325,8 +324,6 @@
 
   std::string Dump();
 
-  void SetVSyncCallback(VSyncCallback callback);
-
   const DisplayParams& GetPrimaryDisplayParams() const {
     return primary_display_;
   }
@@ -350,6 +347,18 @@
   // on/off. Returns true on success, false on failure.
   bool EnableDisplay(const DisplayParams& display, bool enabled);
 
+  class VsyncService : public BnVsyncService {
+   public:
+    status_t registerCallback(const sp<IVsyncCallback> callback) override;
+    status_t unregisterCallback(const sp<IVsyncCallback> callback) override;
+    void OnVsync(int64_t vsync_timestamp);
+   private:
+    std::vector<sp<IVsyncCallback>>::const_iterator FindCallback(
+        const sp<IVsyncCallback>& callback) const;
+    std::mutex mutex_;
+    std::vector<sp<IVsyncCallback>> callbacks_;
+  };
+
   class ComposerCallback : public Hwc2::IComposerCallback {
    public:
     ComposerCallback() = default;
@@ -360,6 +369,7 @@
                                    int64_t timestamp) override;
 
     bool GotFirstHotplug() { return got_first_hotplug_; }
+    void SetVsyncService(const sp<VsyncService>& vsync_service);
 
     struct Displays {
       hwc2_display_t primary_display = 0;
@@ -385,6 +395,7 @@
     DisplayInfo primary_display_;
     std::optional<DisplayInfo> external_display_;
     bool external_display_was_hotplugged_ = false;
+    sp<VsyncService> vsync_service_;
   };
 
   HWC::Error Validate(hwc2_display_t display);
@@ -484,9 +495,6 @@
   // vector must be sorted by surface_id in ascending order.
   std::vector<Layer> layers_;
 
-  // Handler to hook vsync events outside of this class.
-  VSyncCallback vsync_callback_;
-
   // The layer posting thread. This thread wakes up a short time before vsync to
   // hand buffers to hardware composer.
   std::thread post_thread_;
@@ -534,6 +542,9 @@
   DvrConfig post_thread_config_;
   std::mutex shared_config_mutex_;
 
+  bool vsync_trace_parity_ = false;
+  sp<VsyncService> vsync_service_;
+
   static constexpr int kPostThreadInterrupted = 1;
 
   HardwareComposer(const HardwareComposer&) = delete;
diff --git a/libs/vr/libvrflinger/vr_flinger.cpp b/libs/vr/libvrflinger/vr_flinger.cpp
index 26aed4f..b57383a 100644
--- a/libs/vr/libvrflinger/vr_flinger.cpp
+++ b/libs/vr/libvrflinger/vr_flinger.cpp
@@ -23,7 +23,6 @@
 #include "DisplayHardware/ComposerHal.h"
 #include "display_manager_service.h"
 #include "display_service.h"
-#include "vsync_service.h"
 
 namespace android {
 namespace dvr {
@@ -85,16 +84,6 @@
   CHECK_ERROR(!service, error, "Failed to create display manager service.");
   dispatcher_->AddService(service);
 
-  service = android::dvr::VSyncService::Create();
-  CHECK_ERROR(!service, error, "Failed to create vsync service.");
-  dispatcher_->AddService(service);
-
-  display_service_->SetVSyncCallback(
-      std::bind(&android::dvr::VSyncService::VSyncEvent,
-                std::static_pointer_cast<android::dvr::VSyncService>(service),
-                std::placeholders::_1, std::placeholders::_2,
-                std::placeholders::_3));
-
   dispatcher_thread_ = std::thread([this]() {
     prctl(PR_SET_NAME, reinterpret_cast<unsigned long>("VrDispatch"), 0, 0, 0);
     ALOGI("Entering message loop.");
diff --git a/libs/vr/libvrflinger/vsync_service.cpp b/libs/vr/libvrflinger/vsync_service.cpp
deleted file mode 100644
index b8d8b08..0000000
--- a/libs/vr/libvrflinger/vsync_service.cpp
+++ /dev/null
@@ -1,212 +0,0 @@
-#include "vsync_service.h"
-
-#include <hardware/hwcomposer.h>
-#include <log/log.h>
-#include <poll.h>
-#include <sys/prctl.h>
-#include <time.h>
-#include <utils/Trace.h>
-
-#include <dvr/dvr_display_types.h>
-#include <pdx/default_transport/service_endpoint.h>
-#include <private/dvr/clock_ns.h>
-#include <private/dvr/display_protocol.h>
-
-using android::dvr::display::VSyncProtocol;
-using android::dvr::display::VSyncSchedInfo;
-using android::pdx::Channel;
-using android::pdx::Message;
-using android::pdx::MessageInfo;
-using android::pdx::default_transport::Endpoint;
-using android::pdx::rpc::DispatchRemoteMethod;
-
-namespace android {
-namespace dvr {
-
-VSyncService::VSyncService()
-    : BASE("VSyncService", Endpoint::Create(VSyncProtocol::kClientPath)),
-      last_vsync_(0),
-      current_vsync_(0),
-      compositor_time_ns_(0),
-      current_vsync_count_(0) {}
-
-VSyncService::~VSyncService() {}
-
-void VSyncService::VSyncEvent(int64_t timestamp_ns,
-                              int64_t compositor_time_ns,
-                              uint32_t vsync_count) {
-  ATRACE_NAME("VSyncService::VSyncEvent");
-  std::lock_guard<std::mutex> autolock(mutex_);
-
-  last_vsync_ = current_vsync_;
-  current_vsync_ = timestamp_ns;
-  compositor_time_ns_ = compositor_time_ns;
-  current_vsync_count_ = vsync_count;
-
-  NotifyWaiters();
-  UpdateClients();
-}
-
-std::shared_ptr<Channel> VSyncService::OnChannelOpen(pdx::Message& message) {
-  const MessageInfo& info = message.GetInfo();
-
-  auto client = std::make_shared<VSyncChannel>(*this, info.pid, info.cid);
-  AddClient(client);
-
-  return client;
-}
-
-void VSyncService::OnChannelClose(pdx::Message& /*message*/,
-                                  const std::shared_ptr<Channel>& channel) {
-  auto client = std::static_pointer_cast<VSyncChannel>(channel);
-  if (!client) {
-    ALOGW("WARNING: VSyncChannel was NULL!!!\n");
-    return;
-  }
-
-  RemoveClient(client);
-}
-
-void VSyncService::AddWaiter(pdx::Message& message) {
-  std::lock_guard<std::mutex> autolock(mutex_);
-  std::unique_ptr<VSyncWaiter> waiter(new VSyncWaiter(message));
-  waiters_.push_back(std::move(waiter));
-}
-
-void VSyncService::AddClient(const std::shared_ptr<VSyncChannel>& client) {
-  std::lock_guard<std::mutex> autolock(mutex_);
-  clients_.push_back(client);
-}
-
-void VSyncService::RemoveClient(const std::shared_ptr<VSyncChannel>& client) {
-  std::lock_guard<std::mutex> autolock(mutex_);
-  clients_.remove(client);
-}
-
-// Private. Assumes mutex is held.
-void VSyncService::NotifyWaiters() {
-  ATRACE_NAME("VSyncService::NotifyWaiters");
-  auto first = waiters_.begin();
-  auto last = waiters_.end();
-
-  while (first != last) {
-    (*first)->Notify(current_vsync_);
-    waiters_.erase(first++);
-  }
-}
-
-// Private. Assumes mutex is held.
-void VSyncService::UpdateClients() {
-  ATRACE_NAME("VSyncService::UpdateClients");
-  auto first = clients_.begin();
-  auto last = clients_.end();
-
-  while (first != last) {
-    (*first)->Signal();
-    first++;
-  }
-}
-
-pdx::Status<void> VSyncService::HandleMessage(pdx::Message& message) {
-  ATRACE_NAME("VSyncService::HandleMessage");
-  switch (message.GetOp()) {
-    case VSyncProtocol::Wait::Opcode:
-      AddWaiter(message);
-      return {};
-
-    case VSyncProtocol::GetLastTimestamp::Opcode:
-      DispatchRemoteMethod<VSyncProtocol::GetLastTimestamp>(
-          *this, &VSyncService::OnGetLastTimestamp, message);
-      return {};
-
-    case VSyncProtocol::GetSchedInfo::Opcode:
-      DispatchRemoteMethod<VSyncProtocol::GetSchedInfo>(
-          *this, &VSyncService::OnGetSchedInfo, message);
-      return {};
-
-    case VSyncProtocol::Acknowledge::Opcode:
-      DispatchRemoteMethod<VSyncProtocol::Acknowledge>(
-          *this, &VSyncService::OnAcknowledge, message);
-      return {};
-
-    default:
-      return Service::HandleMessage(message);
-  }
-}
-
-pdx::Status<int64_t> VSyncService::OnGetLastTimestamp(pdx::Message& message) {
-  auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
-  std::lock_guard<std::mutex> autolock(mutex_);
-
-  // Getting the timestamp has the side effect of ACKing.
-  client->Ack();
-  return {current_vsync_};
-}
-
-pdx::Status<VSyncSchedInfo> VSyncService::OnGetSchedInfo(
-    pdx::Message& message) {
-  auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
-  std::lock_guard<std::mutex> autolock(mutex_);
-
-  // Getting the timestamp has the side effect of ACKing.
-  client->Ack();
-
-  uint32_t next_vsync_count = current_vsync_count_ + 1;
-  int64_t current_time = GetSystemClockNs();
-  int64_t vsync_period_ns = 0;
-  int64_t next_warp;
-  if (current_vsync_ == 0 || last_vsync_ == 0) {
-    // Handle startup when current_vsync_ or last_vsync_ are 0.
-    // Normally should not happen because vsync_service is running before
-    // applications, but in case it does a sane time prevents applications
-    // from malfunctioning.
-    vsync_period_ns = 20000000;
-    next_warp = current_time;
-  } else {
-    // TODO(jbates) When we have an accurate reading of the true vsync
-    // period, use that instead of this estimated value.
-    vsync_period_ns = current_vsync_ - last_vsync_;
-    // Clamp the period, because when there are no surfaces the last_vsync_
-    // value will get stale. Note this is temporary and goes away as soon
-    // as we have an accurate vsync period reported by the system.
-    vsync_period_ns = std::min(vsync_period_ns, INT64_C(20000000));
-    next_warp = current_vsync_ + vsync_period_ns - compositor_time_ns_;
-    // If the request missed the present window, move up to the next vsync.
-    if (current_time > next_warp) {
-      next_warp += vsync_period_ns;
-      ++next_vsync_count;
-    }
-  }
-
-  return {{vsync_period_ns, next_warp, next_vsync_count}};
-}
-
-pdx::Status<void> VSyncService::OnAcknowledge(pdx::Message& message) {
-  auto client = std::static_pointer_cast<VSyncChannel>(message.GetChannel());
-  std::lock_guard<std::mutex> autolock(mutex_);
-  client->Ack();
-  return {};
-}
-
-void VSyncWaiter::Notify(int64_t timestamp) {
-  timestamp_ = timestamp;
-  DispatchRemoteMethod<VSyncProtocol::Wait>(*this, &VSyncWaiter::OnWait,
-                                            message_);
-}
-
-pdx::Status<int64_t> VSyncWaiter::OnWait(pdx::Message& /*message*/) {
-  return {timestamp_};
-}
-
-void VSyncChannel::Ack() {
-  ALOGD_IF(TRACE > 1, "VSyncChannel::Ack: pid=%d cid=%d\n", pid_, cid_);
-  service_.ModifyChannelEvents(cid_, POLLPRI, 0);
-}
-
-void VSyncChannel::Signal() {
-  ALOGD_IF(TRACE > 1, "VSyncChannel::Signal: pid=%d cid=%d\n", pid_, cid_);
-  service_.ModifyChannelEvents(cid_, 0, POLLPRI);
-}
-
-}  // namespace dvr
-}  // namespace android
diff --git a/libs/vr/libvrflinger/vsync_service.h b/libs/vr/libvrflinger/vsync_service.h
deleted file mode 100644
index 822f02b..0000000
--- a/libs/vr/libvrflinger/vsync_service.h
+++ /dev/null
@@ -1,107 +0,0 @@
-#ifndef ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
-#define ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
-
-#include <pdx/service.h>
-
-#include <list>
-#include <memory>
-#include <mutex>
-#include <thread>
-
-#include "display_service.h"
-
-namespace android {
-namespace dvr {
-
-// VSyncWaiter encapsulates a client blocked waiting for the next vsync.
-// It is used to enqueue the Message to reply to when the next vsync event
-// occurs.
-class VSyncWaiter {
- public:
-  explicit VSyncWaiter(pdx::Message& message) : message_(std::move(message)) {}
-
-  void Notify(int64_t timestamp);
-
- private:
-  pdx::Status<int64_t> OnWait(pdx::Message& message);
-
-  pdx::Message message_;
-  int64_t timestamp_ = 0;
-
-  VSyncWaiter(const VSyncWaiter&) = delete;
-  void operator=(const VSyncWaiter&) = delete;
-};
-
-// VSyncChannel manages the service-side per-client context for each client
-// using the service.
-class VSyncChannel : public pdx::Channel {
- public:
-  VSyncChannel(pdx::Service& service, int pid, int cid)
-      : service_(service), pid_(pid), cid_(cid) {}
-
-  void Ack();
-  void Signal();
-
- private:
-  pdx::Service& service_;
-  pid_t pid_;
-  int cid_;
-
-  VSyncChannel(const VSyncChannel&) = delete;
-  void operator=(const VSyncChannel&) = delete;
-};
-
-// VSyncService implements the displayd vsync service over ServiceFS.
-class VSyncService : public pdx::ServiceBase<VSyncService> {
- public:
-  ~VSyncService() override;
-
-  pdx::Status<void> HandleMessage(pdx::Message& message) override;
-
-  std::shared_ptr<pdx::Channel> OnChannelOpen(pdx::Message& message) override;
-  void OnChannelClose(pdx::Message& message,
-                      const std::shared_ptr<pdx::Channel>& channel) override;
-
-  // Called by the hardware composer HAL, or similar, whenever a vsync event
-  // occurs on the primary display. |compositor_time_ns| is the number of ns
-  // before the next vsync when the compositor will preempt the GPU to do EDS
-  // and lens warp.
-  void VSyncEvent(int64_t timestamp_ns, int64_t compositor_time_ns,
-                  uint32_t vsync_count);
-
- private:
-  friend BASE;
-
-  VSyncService();
-
-  pdx::Status<int64_t> OnGetLastTimestamp(pdx::Message& message);
-  pdx::Status<display::VSyncSchedInfo> OnGetSchedInfo(pdx::Message& message);
-  pdx::Status<void> OnAcknowledge(pdx::Message& message);
-
-  void NotifierThreadFunction();
-
-  void AddWaiter(pdx::Message& message);
-  void NotifyWaiters();
-  void UpdateClients();
-
-  void AddClient(const std::shared_ptr<VSyncChannel>& client);
-  void RemoveClient(const std::shared_ptr<VSyncChannel>& client);
-
-  int64_t last_vsync_;
-  int64_t current_vsync_;
-  int64_t compositor_time_ns_;
-  uint32_t current_vsync_count_;
-
-  std::mutex mutex_;
-
-  std::list<std::unique_ptr<VSyncWaiter>> waiters_;
-  std::list<std::shared_ptr<VSyncChannel>> clients_;
-
-  VSyncService(const VSyncService&) = delete;
-  void operator=(VSyncService&) = delete;
-};
-
-}  // namespace dvr
-}  // namespace android
-
-#endif  // ANDROID_DVR_SERVICES_DISPLAYD_VSYNC_SERVICE_H_
diff --git a/services/surfaceflinger/DispSync.cpp b/services/surfaceflinger/DispSync.cpp
index eb271cd..829b53d 100644
--- a/services/surfaceflinger/DispSync.cpp
+++ b/services/surfaceflinger/DispSync.cpp
@@ -236,14 +236,6 @@
         return BAD_VALUE;
     }
 
-    // This method is only here to handle the !SurfaceFlinger::hasSyncFramework
-    // case.
-    bool hasAnyEventListeners() {
-        if (kTraceDetailedInfo) ATRACE_CALL();
-        Mutex::Autolock lock(mMutex);
-        return !mEventListeners.empty();
-    }
-
 private:
     struct EventListener {
         const char* mName;
@@ -407,22 +399,18 @@
     reset();
     beginResync();
 
-    if (kTraceDetailedInfo) {
-        // If we're not getting present fences then the ZeroPhaseTracer
-        // would prevent HW vsync event from ever being turned off.
-        // Even if we're just ignoring the fences, the zero-phase tracing is
-        // not needed because any time there is an event registered we will
-        // turn on the HW vsync events.
-        if (!mIgnorePresentFences && kEnableZeroPhaseTracer) {
-            mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>();
-            addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get());
-        }
+    if (kTraceDetailedInfo && kEnableZeroPhaseTracer) {
+        mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>();
+        addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get());
     }
 }
 
 void DispSync::reset() {
     Mutex::Autolock lock(mMutex);
+    resetLocked();
+}
 
+void DispSync::resetLocked() {
     mPhase = 0;
     mReferenceTime = 0;
     mModelUpdated = false;
@@ -435,6 +423,10 @@
 bool DispSync::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
     Mutex::Autolock lock(mMutex);
 
+    if (mIgnorePresentFences) {
+        return true;
+    }
+
     mPresentFences[mPresentSampleOffset] = fenceTime;
     mPresentSampleOffset = (mPresentSampleOffset + 1) % NUM_PRESENT_SAMPLES;
     mNumResyncSamplesSincePresent = 0;
@@ -480,12 +472,10 @@
     }
 
     if (mIgnorePresentFences) {
-        // If we don't have the sync framework we will never have
-        // addPresentFence called.  This means we have no way to know whether
+        // If we're ignoring the present fences we have no way to know whether
         // or not we're synchronized with the HW vsyncs, so we just request
-        // that the HW vsync events be turned on whenever we need to generate
-        // SW vsync events.
-        return mThread->hasAnyEventListeners();
+        // that the HW vsync events be turned on.
+        return true;
     }
 
     // Check against kErrorThreshold / 2 to add some hysteresis before having to
@@ -659,6 +649,14 @@
     return (((now - phase) / mPeriod) + periodOffset + 1) * mPeriod + phase;
 }
 
+void DispSync::setIgnorePresentFences(bool ignore) {
+    Mutex::Autolock lock(mMutex);
+    if (mIgnorePresentFences != ignore) {
+        mIgnorePresentFences = ignore;
+        resetLocked();
+    }
+}
+
 void DispSync::dump(String8& result) const {
     Mutex::Autolock lock(mMutex);
     result.appendFormat("present fences are %s\n", mIgnorePresentFences ? "ignored" : "used");
diff --git a/services/surfaceflinger/DispSync.h b/services/surfaceflinger/DispSync.h
index 077256a..c00c161 100644
--- a/services/surfaceflinger/DispSync.h
+++ b/services/surfaceflinger/DispSync.h
@@ -124,12 +124,21 @@
     // the refresh after next. etc.
     nsecs_t computeNextRefresh(int periodOffset) const;
 
+    // In certain situations the present fences aren't a good indicator of vsync
+    // time, e.g. when vr flinger is active, or simply aren't available,
+    // e.g. when the sync framework isn't present. Use this method to toggle
+    // whether or not DispSync ignores present fences. If present fences are
+    // ignored, DispSync will always ask for hardware vsync events by returning
+    // true from addPresentFence() and addResyncSample().
+    void setIgnorePresentFences(bool ignore);
+
     // dump appends human-readable debug info to the result string.
     void dump(String8& result) const;
 
 private:
     void updateModelLocked();
     void updateErrorLocked();
+    void resetLocked();
     void resetErrorLocked();
 
     enum { MAX_RESYNC_SAMPLES = 32 };
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 2d6d470..96d1c76 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1355,6 +1355,8 @@
 
 void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
                                      int64_t timestamp) {
+    ATRACE_NAME("SF onVsync");
+
     Mutex::Autolock lock(mStateLock);
     // Ignore any vsyncs from a previous hardware composer.
     if (sequenceId != getBE().mComposerSequenceId) {
@@ -1366,11 +1368,16 @@
         return;
     }
 
+    if (type != DisplayDevice::DISPLAY_PRIMARY) {
+        // For now, we don't do anything with external display vsyncs.
+        return;
+    }
+
     bool needsHwVsync = false;
 
     { // Scope for the lock
         Mutex::Autolock _l(mHWVsyncLock);
-        if (type == DisplayDevice::DISPLAY_PRIMARY && mPrimaryHWVsyncEnabled) {
+        if (mPrimaryHWVsyncEnabled) {
             needsHwVsync = mPrimaryDispSync.addResyncSample(timestamp);
         }
     }
@@ -1481,8 +1488,6 @@
 
     if (vrFlingerRequestsDisplay) {
         mVrFlinger->GrantDisplayOwnership();
-    } else {
-        enableHardwareVsync();
     }
 
     mVisibleRegionsDirty = true;
@@ -1498,10 +1503,17 @@
     const nsecs_t period = activeConfig->getVsyncPeriod();
     mAnimFrameTracker.setDisplayRefreshPeriod(period);
 
+    // The present fences returned from vr_hwc are not an accurate
+    // representation of vsync times.
+    mPrimaryDispSync.setIgnorePresentFences(
+            getBE().mHwc->isUsingVrComposer() || !hasSyncFramework);
+
     // Use phase of 0 since phase is not known.
     // Use latency of 0, which will snap to the ideal latency.
     setCompositorTimingSnapped(0, period, 0);
 
+    resyncToHardwareVsync(false);
+
     android_atomic_or(1, &mRepaintEverything);
     setTransactionFlags(eDisplayTransactionNeeded);
 }
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index 0c91b07..003775b 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -39,6 +39,10 @@
     "android.hardware.graphics.composer@2.1-hal",
   ],
 
+  export_static_lib_headers: [
+    "libdisplay",
+  ],
+
   export_shared_lib_headers: [
     "android.frameworks.vr.composer@1.0",
     "android.hardware.graphics.composer@2.1",
@@ -48,6 +52,7 @@
 
   cflags: [
     "-DLOG_TAG=\"vr_hwc\"",
+    "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
     "-Wall",
     "-Werror",
     // mVrClient unused in vr_composer_client.cpp
diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp
index 4af47d2..d1ed12b 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.cpp
+++ b/services/vr/hardware_composer/impl/vr_hwc.cpp
@@ -16,9 +16,11 @@
 #include "impl/vr_hwc.h"
 
 #include "android-base/stringprintf.h"
+#include <binder/IServiceManager.h>
 #include <cutils/properties.h>
 #include <private/dvr/display_client.h>
 #include <ui/Fence.h>
+#include <utils/Trace.h>
 
 #include <mutex>
 
@@ -244,29 +246,38 @@
 ////////////////////////////////////////////////////////////////////////////////
 // VrHwcClient
 
-VrHwc::VrHwc() {}
+VrHwc::VrHwc() {
+  vsync_callback_ = new VsyncCallback;
+}
 
-VrHwc::~VrHwc() {}
+VrHwc::~VrHwc() {
+  vsync_callback_->SetEventCallback(nullptr);
+}
 
 bool VrHwc::hasCapability(hwc2_capability_t /* capability */) { return false; }
 
 void VrHwc::registerEventCallback(EventCallback* callback) {
-  {
-    std::lock_guard<std::mutex> guard(mutex_);
-    event_callback_ = callback;
-    int32_t width, height;
-    GetPrimaryDisplaySize(&width, &height);
-    // Create the primary display late to avoid initialization issues between
-    // VR HWC and SurfaceFlinger.
-    displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height));
-  }
+  std::unique_lock<std::mutex> lock(mutex_);
+  event_callback_ = callback;
+  int32_t width, height;
+  GetPrimaryDisplaySize(&width, &height);
+  // Create the primary display late to avoid initialization issues between
+  // VR HWC and SurfaceFlinger.
+  displays_[kDefaultDisplayId].reset(new HwcDisplay(width, height));
+
+  // Surface flinger will make calls back into vr_hwc when it receives the
+  // onHotplug() call, so it's important to release mutex_ here.
+  lock.unlock();
   event_callback_->onHotplug(kDefaultDisplayId,
                              IComposerCallback::Connection::CONNECTED);
+  lock.lock();
+  UpdateVsyncCallbackEnabledLocked();
 }
 
 void VrHwc::unregisterEventCallback() {
   std::lock_guard<std::mutex> guard(mutex_);
   event_callback_ = nullptr;
+  UpdateVsyncCallbackEnabledLocked();
 }
 
 uint32_t VrHwc::getMaxVirtualDisplayCount() { return 1; }
@@ -475,8 +486,45 @@
   if (!display_ptr)
     return Error::BAD_DISPLAY;
 
-  display_ptr->set_vsync_enabled(enabled);
-  return Error::NONE;
+  if (enabled != IComposerClient::Vsync::ENABLE &&
+      enabled != IComposerClient::Vsync::DISABLE) {
+    return Error::BAD_PARAMETER;
+  }
+
+  Error set_vsync_result = Error::NONE;
+  if (display == kDefaultDisplayId) {
+    sp<IVsyncService> vsync_service = interface_cast<IVsyncService>(
+        defaultServiceManager()->getService(
+            String16(IVsyncService::GetServiceName())));
+    if (vsync_service == nullptr) {
+      ALOGE("Failed to get vsync service");
+      return Error::NO_RESOURCES;
+    }
+
+    if (enabled == IComposerClient::Vsync::ENABLE) {
+      ALOGI("Enable vsync");
+      display_ptr->set_vsync_enabled(true);
+      status_t result = vsync_service->registerCallback(vsync_callback_);
+      if (result != OK) {
+        ALOGE("%s service registerCallback() failed: %s (%d)",
+            IVsyncService::GetServiceName(), strerror(-result), result);
+        set_vsync_result = Error::NO_RESOURCES;
+      }
+    } else if (enabled == IComposerClient::Vsync::DISABLE) {
+      ALOGI("Disable vsync");
+      display_ptr->set_vsync_enabled(false);
+      status_t result = vsync_service->unregisterCallback(vsync_callback_);
+      if (result != OK) {
+        ALOGE("%s service unregisterCallback() failed: %s (%d)",
+            IVsyncService::GetServiceName(), strerror(-result), result);
+        set_vsync_result = Error::NO_RESOURCES;
+      }
+    }
+
+    UpdateVsyncCallbackEnabledLocked();
+  }
+
+  return set_vsync_result;
 }
 
 Error VrHwc::setColorTransform(Display display, const float* matrix,
@@ -559,7 +607,8 @@
   frame.display_height = display_ptr->height();
   frame.active_config = display_ptr->active_config();
   frame.power_mode = display_ptr->power_mode();
-  frame.vsync_enabled = display_ptr->vsync_enabled();
+  frame.vsync_enabled = display_ptr->vsync_enabled() ?
+      IComposerClient::Vsync::ENABLE : IComposerClient::Vsync::DISABLE;
   frame.color_transform_hint = display_ptr->color_transform_hint();
   frame.color_mode = display_ptr->color_mode();
   memcpy(frame.color_transform, display_ptr->color_transform(),
@@ -911,6 +960,15 @@
   return iter == displays_.end() ? nullptr : iter->second.get();
 }
 
+void VrHwc::UpdateVsyncCallbackEnabledLocked() {
+  auto primary_display = FindDisplay(kDefaultDisplayId);
+  LOG_ALWAYS_FATAL_IF(event_callback_ != nullptr && primary_display == nullptr,
+      "Should have created the primary display by now");
+  bool send_vsync =
+      event_callback_ != nullptr && primary_display->vsync_enabled();
+  vsync_callback_->SetEventCallback(send_vsync ? event_callback_ : nullptr);
+}
+
 void HwcLayer::dumpDebugInfo(std::string* result) const {
   if (!result) {
     return;
@@ -928,5 +986,18 @@
       buffer_metadata.layerCount, buffer_metadata.format);
 }
 
+status_t VrHwc::VsyncCallback::onVsync(int64_t vsync_timestamp) {
+  ATRACE_NAME("vr_hwc onVsync");
+  std::lock_guard<std::mutex> guard(mutex_);
+  if (callback_ != nullptr)
+    callback_->onVsync(kDefaultDisplayId, vsync_timestamp);
+  return NO_ERROR;
+}
+
+void VrHwc::VsyncCallback::SetEventCallback(EventCallback* callback) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  callback_ = callback;
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h
index 85e587a..f9872b2 100644
--- a/services/vr/hardware_composer/impl/vr_hwc.h
+++ b/services/vr/hardware_composer/impl/vr_hwc.h
@@ -20,6 +20,7 @@
 #include <android/frameworks/vr/composer/1.0/IVrComposerClient.h>
 #include <android/hardware/graphics/composer/2.1/IComposer.h>
 #include <composer-hal/2.1/ComposerHal.h>
+#include <private/dvr/vsync_service.h>
 #include <ui/Fence.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/StrongPointer.h>
@@ -156,10 +157,8 @@
   IComposerClient::PowerMode power_mode() const { return power_mode_; }
   void set_power_mode(IComposerClient::PowerMode mode) { power_mode_ = mode; }
 
-  IComposerClient::Vsync vsync_enabled() const { return vsync_enabled_; }
-  void set_vsync_enabled(IComposerClient::Vsync vsync) {
-    vsync_enabled_ = vsync;
-  }
+  bool vsync_enabled() const { return vsync_enabled_; }
+  void set_vsync_enabled(bool vsync) {vsync_enabled_ = vsync;}
 
   const float* color_transform() const { return color_transform_; }
   int32_t color_transform_hint() const { return color_transform_hint_; }
@@ -187,7 +186,7 @@
   Config active_config_;
   ColorMode color_mode_;
   IComposerClient::PowerMode power_mode_;
-  IComposerClient::Vsync vsync_enabled_;
+  bool vsync_enabled_ = false;
   float color_transform_[16];
   int32_t color_transform_hint_;
 
@@ -299,8 +298,23 @@
   void UnregisterObserver(Observer* observer) override;
 
  private:
+  class VsyncCallback : public BnVsyncCallback {
+   public:
+    status_t onVsync(int64_t vsync_timestamp) override;
+    void SetEventCallback(EventCallback* callback);
+   private:
+    std::mutex mutex_;
+    EventCallback* callback_;
+  };
+
   HwcDisplay* FindDisplay(Display display);
 
+  // Re-evaluate whether or not we should start making onVsync() callbacks to
+  // the client. We need enableCallback(true) to have been called, and
+  // setVsyncEnabled() to have been called for the primary display. The caller
+  // must have mutex_ locked already.
+  void UpdateVsyncCallbackEnabledLocked();
+
   wp<VrComposerClient> client_;
 
   // Guard access to internal state from binder threads.
@@ -312,6 +326,8 @@
   EventCallback* event_callback_ = nullptr;
   Observer* observer_ = nullptr;
 
+  sp<VsyncCallback> vsync_callback_;
+
   VrHwc(const VrHwc&) = delete;
   void operator=(const VrHwc&) = delete;
 };