SF: Tie VSyncState lifetime to display hotplug

This CL creates/destroys EventThread::VSyncState when the display is
connected/disconnected.

Bug: 74619554
Test: libsurfaceflinger_unittest
Test: dumpsys SurfaceFlinger --vsync
Change-Id: I46dad4857222b6d2932ab568c4d66325edc3371b
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 8201704..2c1833b 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -118,6 +118,7 @@
 
     TestableSurfaceFlinger mFlinger;
     mock::EventThread* mEventThread = new mock::EventThread();
+    mock::EventThread* mSFEventThread = new mock::EventThread();
     mock::EventControlThread* mEventControlThread = new mock::EventControlThread();
     sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
     sp<GraphicBuffer> mBuffer = new GraphicBuffer();
@@ -161,6 +162,7 @@
 
     mFlinger.mutableEventControlThread().reset(mEventControlThread);
     mFlinger.mutableEventThread().reset(mEventThread);
+    mFlinger.mutableSFEventThread().reset(mSFEventThread);
     mFlinger.mutableEventQueue().reset(mMessageQueue);
     mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
     mFlinger.mutableInterceptor().reset(mSurfaceInterceptor);
@@ -319,6 +321,11 @@
     // Whether the display is primary
     static constexpr Primary PRIMARY = primary;
 
+    static constexpr auto displayType() {
+        return static_cast<bool>(PRIMARY) ? EventThread::DisplayType::Primary
+                                          : EventThread::DisplayType::External;
+    }
+
     static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
         auto injector =
                 FakeDisplayDeviceInjector(test->mFlinger, DISPLAY_ID::get(),
@@ -1404,23 +1411,17 @@
     Case::PerFrameMetadataSupport::setupComposerCallExpectations(this);
 
     EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1);
-    EXPECT_CALL(*mEventThread,
-                onHotplugReceived(static_cast<bool>(Case::Display::PRIMARY)
-                                          ? EventThread::DisplayType::Primary
-                                          : EventThread::DisplayType::External,
-                                  true))
-            .Times(1);
+
+    EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::displayType(), true)).Times(1);
+    EXPECT_CALL(*mSFEventThread, onHotplugReceived(Case::Display::displayType(), true)).Times(1);
 }
 
 template <typename Case>
 void HandleTransactionLockedTest::setupCommonCallExpectationsForDisconnectProcessing() {
     EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
-    EXPECT_CALL(*mEventThread,
-                onHotplugReceived(static_cast<bool>(Case::Display::PRIMARY)
-                                          ? EventThread::DisplayType::Primary
-                                          : EventThread::DisplayType::External,
-                                  false))
-            .Times(1);
+
+    EXPECT_CALL(*mEventThread, onHotplugReceived(Case::Display::displayType(), false)).Times(1);
+    EXPECT_CALL(*mSFEventThread, onHotplugReceived(Case::Display::displayType(), false)).Times(1);
 }
 
 template <typename Case>
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index e6cdd02..79fb034 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -104,6 +104,10 @@
 
     createThread();
     mConnection = createConnection(mConnectionEventCallRecorder);
+
+    // A display must be connected for VSYNC events to be delivered.
+    mThread->onHotplugReceived(EventThread::DisplayType::Primary, true);
+    expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, true);
 }
 
 EventThreadTest::~EventThreadTest() {
@@ -207,6 +211,23 @@
     EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
 }
 
+TEST_F(EventThreadTest, vsyncRequestIsIgnoredIfDisplayIsDisconnected) {
+    mThread->onHotplugReceived(EventThread::DisplayType::Primary, false);
+    expectHotplugEventReceivedByConnection(EventThread::DisplayType::Primary, false);
+
+    // Signal that we want the next vsync event to be posted to the connection.
+    mThread->requestNextVsync(mConnection, false);
+
+    // EventThread should not enable vsync callbacks.
+    EXPECT_FALSE(mVSyncSetEnabledCallRecorder.waitForUnexpectedCall().has_value());
+
+    // Use the received callback to signal a vsync event.
+    // The event should not be received by the interceptor nor the connection.
+    mCallback->onVSyncEvent(123);
+    EXPECT_FALSE(mInterceptVSyncCallRecorder.waitForCall(0us).has_value());
+    EXPECT_FALSE(mConnectionEventCallRecorder.waitForCall(0us).has_value());
+}
+
 TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
     // Signal that we want the next vsync event to be posted to the connection
     mThread->requestNextVsync(mConnection, false);
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 2a8dda6..9c5c967 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -290,6 +290,7 @@
     auto& mutableEventControlThread() { return mFlinger->mEventControlThread; }
     auto& mutableEventQueue() { return mFlinger->mEventQueue; }
     auto& mutableEventThread() { return mFlinger->mEventThread; }
+    auto& mutableSFEventThread() { return mFlinger->mSFEventThread; }
     auto& mutableGeometryInvalid() { return mFlinger->mGeometryInvalid; }
     auto& mutableHWVsyncAvailable() { return mFlinger->mHWVsyncAvailable; }
     auto& mutableInterceptor() { return mFlinger->mInterceptor; }
@@ -317,6 +318,7 @@
         mutableEventControlThread().reset();
         mutableEventQueue().reset();
         mutableEventThread().reset();
+        mutableSFEventThread().reset();
         mutableInterceptor().reset();
         mutablePrimaryDispSync().reset();
         mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());