composer 2.4: add api to control vsync period

Add new functions to improve vsync period switching by the platform:
 - Adding a list of supported vsync periods to Config to avoid the need to expose
   separate Configs for each vsync period.
 - Adding an API to set the vsync period with timeline constraints to allow better
   synchronization with vsync period change.
 - Extending onVsync() callback to provide the current vsync period.

Test: rev up composer to 2.4 and test refresh rate switching
Bug: 141329414
Change-Id: I1a6f395d9634edadc68649d02f624f00173ec519
diff --git a/graphics/composer/2.4/Android.bp b/graphics/composer/2.4/Android.bp
index 0e1bc09..5f700be 100644
--- a/graphics/composer/2.4/Android.bp
+++ b/graphics/composer/2.4/Android.bp
@@ -7,7 +7,9 @@
         enabled: true,
     },
     srcs: [
+        "types.hal",
         "IComposer.hal",
+        "IComposerCallback.hal",
         "IComposerClient.hal",
     ],
     interfaces: [
diff --git a/graphics/composer/2.4/IComposerCallback.hal b/graphics/composer/2.4/IComposerCallback.hal
new file mode 100644
index 0000000..5c3f8bd
--- /dev/null
+++ b/graphics/composer/2.4/IComposerCallback.hal
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.graphics.composer@2.4;
+
+import @2.1::Display;
+import @2.1::IComposerCallback;
+
+interface IComposerCallback extends @2.1::IComposerCallback {
+    /**
+     * Notifies the client that a vsync event has occurred. This callback must
+     * only be triggered when vsync is enabled for this display (through
+     * setVsyncEnabled).
+     *
+     * @param display is the display which has received a vsync event
+     * @param timestamp is the CLOCK_MONOTONIC time at which the vsync event
+     *        occurred, in nanoseconds.
+     * @param vsyncPeriodNanos is the display vsync period in nanoseconds i.e. the next onVsync_2_4
+     *        is expected to be called vsyncPeriodNanos nanoseconds after this call.
+     */
+    oneway onVsync_2_4(Display display, int64_t timestamp, VsyncPeriodNanos vsyncPeriodNanos);
+};
diff --git a/graphics/composer/2.4/IComposerClient.hal b/graphics/composer/2.4/IComposerClient.hal
index 60445f5..c2102d5 100644
--- a/graphics/composer/2.4/IComposerClient.hal
+++ b/graphics/composer/2.4/IComposerClient.hal
@@ -16,6 +16,8 @@
 
 package android.hardware.graphics.composer@2.4;
 
+import IComposerCallback;
+import @2.1::Config;
 import @2.1::Display;
 import @2.1::Error;
 import @2.3::IComposerClient;
@@ -50,6 +52,32 @@
     };
 
     /**
+     * Constraints for changing vsync period.
+     */
+    struct VsyncPeriodChangeConstraints {
+        /**
+         * Time in CLOCK_MONOTONIC after which the vsync period may change
+         * (i.e., the vsync period must not change before this time).
+         */
+        int64_t desiredTimeNanos;
+        /**
+         * If true, requires that the vsync period change must happen seamlessly without
+         * a noticeable visual artifact.
+         */
+        bool seamlessRequired;
+    };
+
+    /**
+     * Provides a IComposerCallback object for the device to call.
+     *
+     * This function must be called only once.
+     *
+     * @param callback is the IComposerCallback object.
+     */
+    @entry
+    registerCallback_2_4(IComposerCallback callback);
+
+    /**
      * Provides a list of supported capabilities (as described in the
      * definition of DisplayCapability above). This list must not change after
      * initialization.
@@ -69,4 +97,63 @@
      * @return type is the connection type of the display.
      */
     getDisplayConnectionType(Display display) generates (Error error, DisplayConnectionType type);
+
+    /**
+     * Provides a list of the vsync periods supported by the display in the given configuration
+     *
+     * @param display is the display for which the vsync periods are queried.
+     * @param config is the display configuration for which the vsync periods are queried.
+     *
+     * @return error is NONE upon success. Otherwise,
+     *     BAD_DISPLAY when an invalid display handle was passed in.
+     *     BAD_CONFIG when an invalid config handle was passed in.
+     * @return supportedVsyncPeriods is a list of supported vsync periods.
+     */
+    getSupportedDisplayVsyncPeriods(Display display, Config config)
+        generates (Error error, vec<VsyncPeriodNanos> supportedVsyncPeriods);
+
+    /**
+     * Retrieves which vsync period the display is currently using.
+     *
+     * If no display configuration is currently active, this function must
+     * return BAD_CONFIG. If the vsync period is about to change due to a
+     * setActiveConfigAndVsyncPeriod call, this function must return the current vsync period
+     * until the change takes place.
+     *
+     * @param display is the display for which the vsync period is queried.
+     * @return error is NONE upon success. Otherwise,
+     *         BAD_DISPLAY when an invalid display handle was passed in.
+     *         BAD_CONFIG when no configuration is currently active.
+     * @return vsyncPeriodNanos is the current vsync period of the display.
+     */
+    getDisplayVsyncPeriod(Display display)
+        generates (Error error, VsyncPeriodNanos vsyncPeriodNanos);
+
+    /**
+     * Sets the active configuration and the refresh rate for this display.
+     * If the config is the same as the current config, only the vsync period shall change.
+     * Upon returning, the given display configuration, except vsync period, must be active and
+     * remain so until either this function is called again or the display is disconnected.
+     * When the display starts to refresh at the new vsync period, onVsync_2_4 callback must be
+     * called with the new vsync period.
+     *
+     * @param display is the display for which the active config is set.
+     * @param config is the new display configuration.
+     * @param vsyncPeriodNanos is the new display vsync period.
+     * @param vsyncPeriodChangeConstraints are the constraints required for changing vsync period.
+     *
+     * @return error is NONE upon success. Otherwise,
+     *         BAD_DISPLAY when an invalid display handle was passed in.
+     *         BAD_CONFIG when the configuration handle passed in is not valid
+     *                    for this display.
+     *         BAD_VSYNC_PERIOD when an invalid vsync period is passed in.
+     *         SEAMLESS_NOT_POSSIBLE when seamlessRequired was true but the display cannot achieve
+     *                               the vsync period change without a noticeable visual artifact.
+     * @return newVsyncAppliedTime is the time in CLOCK_MONOTONIC when the new display will start to
+     *                             refresh at the new vsync period.
+     */
+    setActiveConfigAndVsyncPeriod(Display display, Config config,
+        VsyncPeriodNanos vsyncPeriodNanos,
+        VsyncPeriodChangeConstraints vsyncPeriodChangeConstraints)
+        generates (Error error, int64_t newVsyncAppliedTime);
 };
diff --git a/graphics/composer/2.4/types.hal b/graphics/composer/2.4/types.hal
new file mode 100644
index 0000000..b45d7a6
--- /dev/null
+++ b/graphics/composer/2.4/types.hal
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.graphics.composer@2.4;
+
+import @2.1::Error;
+
+enum Error : @2.1::Error {
+    /**
+     * Invalid vsync period
+     */
+    BAD_VSYNC_PERIOD = 9,
+    /**
+     * Seamless requirements cannot be met
+     */
+    SEAMLESS_NOT_POSSIBLE = 10,
+};
+
+typedef uint32_t VsyncPeriodNanos;
diff --git a/graphics/composer/2.4/utils/command-buffer/include/composer-command-buffer/2.4/ComposerCommandBuffer.h b/graphics/composer/2.4/utils/command-buffer/include/composer-command-buffer/2.4/ComposerCommandBuffer.h
index 6b64c16..cb391be 100644
--- a/graphics/composer/2.4/utils/command-buffer/include/composer-command-buffer/2.4/ComposerCommandBuffer.h
+++ b/graphics/composer/2.4/utils/command-buffer/include/composer-command-buffer/2.4/ComposerCommandBuffer.h
@@ -34,7 +34,7 @@
 namespace V2_4 {
 
 using android::hardware::MessageQueue;
-using android::hardware::graphics::composer::V2_1::Error;
+using android::hardware::graphics::composer::V2_4::Error;
 using android::hardware::graphics::composer::V2_4::IComposerClient;
 
 // This class helps build a command queue.  Note that all sizes/lengths are in
diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
index c810186..ddf209b 100644
--- a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
+++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerClient.h
@@ -20,8 +20,10 @@
 #warning "ComposerClient.h included without LOG_TAG"
 #endif
 
+#include <android/hardware/graphics/composer/2.4/IComposerCallback.h>
 #include <android/hardware/graphics/composer/2.4/IComposerClient.h>
 #include <composer-hal/2.4/ComposerHal.h>
+#include <composer-resources/2.1/ComposerResources.h>
 
 namespace android {
 namespace hardware {
@@ -38,6 +40,54 @@
   public:
     ComposerClientImpl(Hal* hal) : BaseType2_3(hal) {}
 
+    ~ComposerClientImpl() override { mHal->unregisterEventCallback_2_4(); }
+
+    class HalEventCallback : public Hal::EventCallback_2_4 {
+      public:
+        HalEventCallback(const sp<IComposerCallback> callback,
+                         V2_1::hal::ComposerResources* resources)
+            : mCallback(callback), mResources(resources) {}
+
+        void onHotplug(Display display, IComposerCallback::Connection connected) override {
+            if (connected == IComposerCallback::Connection::CONNECTED) {
+                mResources->addPhysicalDisplay(display);
+            } else if (connected == IComposerCallback::Connection::DISCONNECTED) {
+                mResources->removeDisplay(display);
+            }
+
+            auto ret = mCallback->onHotplug(display, connected);
+            ALOGE_IF(!ret.isOk(), "failed to send onHotplug: %s", ret.description().c_str());
+        }
+
+        void onRefresh(Display display) override {
+            mResources->setDisplayMustValidateState(display, true);
+            auto ret = mCallback->onRefresh(display);
+            ALOGE_IF(!ret.isOk(), "failed to send onRefresh: %s", ret.description().c_str());
+        }
+
+        void onVsync(Display display, int64_t timestamp) override {
+            auto ret = mCallback->onVsync(display, timestamp);
+            ALOGE_IF(!ret.isOk(), "failed to send onVsync: %s", ret.description().c_str());
+        }
+
+        void onVsync_2_4(Display display, int64_t timestamp,
+                         VsyncPeriodNanos vsyncPeriodNanos) override {
+            auto ret = mCallback->onVsync_2_4(display, timestamp, vsyncPeriodNanos);
+            ALOGE_IF(!ret.isOk(), "failed to send onVsync_2_4: %s", ret.description().c_str());
+        }
+
+      protected:
+        const sp<IComposerCallback> mCallback;
+        V2_1::hal::ComposerResources* const mResources;
+    };
+
+    Return<void> registerCallback_2_4(const sp<IComposerCallback>& callback) override {
+        // no locking as we require this function to be called only once
+        mHalEventCallback_2_4 = std::make_unique<HalEventCallback>(callback, mResources.get());
+        mHal->registerEventCallback_2_4(mHalEventCallback_2_4.get());
+        return Void();
+    }
+
     Return<void> getDisplayCapabilities_2_4(
             Display display, IComposerClient::getDisplayCapabilities_2_4_cb hidl_cb) override {
         std::vector<IComposerClient::DisplayCapability> capabilities;
@@ -54,6 +104,36 @@
         return Void();
     }
 
+    Return<void> getSupportedDisplayVsyncPeriods(
+            Display display, Config config,
+            IComposerClient::getSupportedDisplayVsyncPeriods_cb hidl_cb) override {
+        std::vector<VsyncPeriodNanos> supportedVsyncPeriods;
+        Error error =
+                mHal->getSupportedDisplayVsyncPeriods(display, config, &supportedVsyncPeriods);
+        hidl_cb(error, supportedVsyncPeriods);
+        return Void();
+    }
+
+    Return<void> getDisplayVsyncPeriod(Display display,
+                                       IComposerClient::getDisplayVsyncPeriod_cb hidl_cb) override {
+        VsyncPeriodNanos vsyncPeriods;
+        Error error = mHal->getDisplayVsyncPeriod(display, &vsyncPeriods);
+        hidl_cb(error, vsyncPeriods);
+        return Void();
+    }
+
+    Return<void> setActiveConfigAndVsyncPeriod(
+            Display display, Config config, VsyncPeriodNanos vsyncPeriodNanos,
+            const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+            IComposerClient::setActiveConfigAndVsyncPeriod_cb hidl_cb) override {
+        int64_t newVsyncAppliedTime = 0;
+        Error error = mHal->setActiveConfigAndVsyncPeriod(display, config, vsyncPeriodNanos,
+                                                          vsyncPeriodChangeConstraints,
+                                                          &newVsyncAppliedTime);
+        hidl_cb(error, newVsyncAppliedTime);
+        return Void();
+    }
+
     static std::unique_ptr<ComposerClientImpl> create(Hal* hal) {
         auto client = std::make_unique<ComposerClientImpl>(hal);
         return client->init() ? std::move(client) : nullptr;
@@ -63,6 +143,8 @@
     using BaseType2_3 = V2_3::hal::detail::ComposerClientImpl<Interface, Hal>;
     using BaseType2_1 = V2_1::hal::detail::ComposerClientImpl<Interface, Hal>;
     using BaseType2_1::mHal;
+    std::unique_ptr<HalEventCallback> mHalEventCallback_2_4;
+    using BaseType2_1::mResources;
 };
 
 }  // namespace detail
diff --git a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h
index c3bb535..0739f62 100644
--- a/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h
+++ b/graphics/composer/2.4/utils/hal/include/composer-hal/2.4/ComposerHal.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <android/hardware/graphics/composer/2.4/types.h>
 #include <composer-hal/2.3/ComposerHal.h>
 
 namespace android {
@@ -30,16 +31,39 @@
 using common::V1_2::Dataspace;
 using common::V1_2::Hdr;
 using common::V1_2::PixelFormat;
+using V2_1::Config;
 using V2_1::Display;
-using V2_1::Error;
 using V2_1::Layer;
+using V2_4::Error;
+using V2_4::VsyncPeriodNanos;
 
 class ComposerHal : public V2_3::hal::ComposerHal {
   public:
+    class EventCallback_2_4 {
+      public:
+        virtual ~EventCallback_2_4() = default;
+        virtual void onHotplug(Display display, IComposerCallback::Connection connected) = 0;
+        virtual void onRefresh(Display display) = 0;
+        virtual void onVsync(Display display, int64_t timestamp) = 0;
+        virtual void onVsync_2_4(Display display, int64_t timestamp,
+                                 VsyncPeriodNanos vsyncPeriodNanos) = 0;
+    };
+
+    virtual void registerEventCallback_2_4(EventCallback_2_4* callback) = 0;
+
+    virtual void unregisterEventCallback_2_4() = 0;
+
     virtual Error getDisplayCapabilities_2_4(
             Display display, std::vector<IComposerClient::DisplayCapability>* outCapabilities) = 0;
     virtual Error getDisplayConnectionType(Display display,
                                            IComposerClient::DisplayConnectionType* outType) = 0;
+    virtual Error getSupportedDisplayVsyncPeriods(
+            Display display, Config config, std::vector<VsyncPeriodNanos>* outVsyncPeriod) = 0;
+    virtual Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) = 0;
+    virtual Error setActiveConfigAndVsyncPeriod(
+            Display display, Config config, VsyncPeriodNanos vsyncPeriodNanos,
+            const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+            int64_t* outNewVsyncAppliedTime) = 0;
 };
 
 }  // namespace hal
diff --git a/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h b/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h
index fd05f66..3420c8c 100644
--- a/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h
+++ b/graphics/composer/2.4/utils/passthrough/include/composer-passthrough/2.4/HwcHal.h
@@ -22,6 +22,7 @@
 
 #include <type_traits>
 
+#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
 #include <composer-hal/2.4/ComposerHal.h>
 #include <composer-passthrough/2.3/HwcHal.h>
 
@@ -39,18 +40,49 @@
 using common::V1_2::Dataspace;
 using common::V1_2::Hdr;
 using common::V1_2::PixelFormat;
+using V2_1::Config;
 using V2_1::Display;
-using V2_1::Error;
+using V2_4::Error;
 
 // HwcHalImpl implements V2_*::hal::ComposerHal on top of hwcomposer2
 template <typename Hal>
 class HwcHalImpl : public V2_3::passthrough::detail::HwcHalImpl<Hal> {
   public:
+    void registerEventCallback_2_4(hal::ComposerHal::EventCallback_2_4* callback) override {
+        mEventCallback_2_4 = callback;
+
+        mDispatch.registerCallback(mDevice, HWC2_CALLBACK_HOTPLUG, this,
+                                   reinterpret_cast<hwc2_function_pointer_t>(hotplugHook));
+        mDispatch.registerCallback(mDevice, HWC2_CALLBACK_REFRESH, this,
+                                   reinterpret_cast<hwc2_function_pointer_t>(refreshHook));
+        mDispatch.registerCallback(mDevice, HWC2_CALLBACK_VSYNC, this,
+                                   reinterpret_cast<hwc2_function_pointer_t>(vsyncHook));
+        mDispatch.registerCallback(mDevice, HWC2_CALLBACK_VSYNC_2_4, this,
+                                   reinterpret_cast<hwc2_function_pointer_t>(vsync_2_4_Hook));
+    }
+
+    void unregisterEventCallback_2_4() override {
+        // we assume the callback functions
+        //
+        //  - can be unregistered
+        //  - can be in-flight
+        //  - will never be called afterward
+        //
+        // which is likely incorrect
+        mDispatch.registerCallback(mDevice, HWC2_CALLBACK_HOTPLUG, this, nullptr);
+        mDispatch.registerCallback(mDevice, HWC2_CALLBACK_REFRESH, this, nullptr);
+        mDispatch.registerCallback(mDevice, HWC2_CALLBACK_VSYNC, this, nullptr);
+        mDispatch.registerCallback(mDevice, HWC2_CALLBACK_VSYNC_2_4, this, nullptr);
+
+        mEventCallback_2_4 = nullptr;
+    }
+
     Error getDisplayCapabilities_2_4(
             Display display,
             std::vector<IComposerClient::DisplayCapability>* outCapabilities) override {
         std::vector<V2_3::IComposerClient::DisplayCapability> capabilities;
-        Error error = BaseType2_3::getDisplayCapabilities(display, &capabilities);
+        V2_3::Error error_2_3 = BaseType2_3::getDisplayCapabilities(display, &capabilities);
+        Error error = static_cast<Error>(error_2_3);
         if (error != Error::NONE) {
             return error;
         }
@@ -74,6 +106,63 @@
         return static_cast<Error>(error);
     }
 
+    Error getSupportedDisplayVsyncPeriods(Display display, Config config,
+                                          std::vector<VsyncPeriodNanos>* outVsyncPeriods) override {
+        if (!mDispatch.getSupportedDisplayVsyncPeriods) {
+            return Error::UNSUPPORTED;
+        }
+
+        uint32_t count = 0;
+        int32_t error = mDispatch.getSupportedDisplayVsyncPeriods(mDevice, display, config, &count,
+                                                                  nullptr);
+        if (error != HWC2_ERROR_NONE) {
+            return static_cast<Error>(error);
+        }
+        outVsyncPeriods->resize(count);
+        error = mDispatch.getSupportedDisplayVsyncPeriods(mDevice, display, config, &count,
+                                                          outVsyncPeriods->data());
+        if (error != HWC2_ERROR_NONE) {
+            *outVsyncPeriods = std::vector<VsyncPeriodNanos>();
+            return static_cast<Error>(error);
+        }
+        return Error::NONE;
+    }
+
+    Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override {
+        if (!mDispatch.getDisplayVsyncPeriod) {
+            return Error::UNSUPPORTED;
+        }
+
+        int32_t error = mDispatch.getDisplayVsyncPeriod(mDevice, display, outVsyncPeriod);
+        if (error != HWC2_ERROR_NONE) {
+            return static_cast<Error>(error);
+        }
+        return Error::NONE;
+    }
+
+    Error setActiveConfigAndVsyncPeriod(
+            Display display, Config config, VsyncPeriodNanos vsyncPeriodNanos,
+            const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+            int64_t* outNewVsyncAppliedTime) override {
+        if (!mDispatch.setActiveConfigAndVsyncPeriod) {
+            return Error::UNSUPPORTED;
+        }
+
+        hwc_vsync_period_change_constraints_t vsync_period_change_constraints;
+        vsync_period_change_constraints.desiredTimeNanos =
+                vsyncPeriodChangeConstraints.desiredTimeNanos;
+        vsync_period_change_constraints.seamlessRequired =
+                vsyncPeriodChangeConstraints.seamlessRequired;
+
+        int32_t error = mDispatch.setActiveConfigAndVsyncPeriod(
+                mDevice, display, config, vsyncPeriodNanos, &vsync_period_change_constraints,
+                outNewVsyncAppliedTime);
+        if (error != HWC2_ERROR_NONE) {
+            return static_cast<Error>(error);
+        }
+        return Error::NONE;
+    }
+
   protected:
     bool initDispatch() override {
         if (!BaseType2_3::initDispatch()) {
@@ -82,14 +171,51 @@
 
         this->initOptionalDispatch(HWC2_FUNCTION_GET_DISPLAY_CONNECTION_TYPE,
                                    &mDispatch.getDisplayConnectionType);
+        this->initOptionalDispatch(HWC2_FUNCTION_REGISTER_CALLBACK, &mDispatch.registerCallback);
+        this->initOptionalDispatch(HWC2_FUNCTION_GET_SUPPORTED_DISPLAY_VSYNC_PERIODS,
+                                   &mDispatch.getSupportedDisplayVsyncPeriods);
+        this->initOptionalDispatch(HWC2_FUNCTION_GET_DISPLAY_VSYNC_PERIOD,
+                                   &mDispatch.getDisplayVsyncPeriod);
+        this->initOptionalDispatch(HWC2_FUNCTION_SET_ACTIVE_CONFIG_AND_VSYNC_PERIOD,
+                                   &mDispatch.setActiveConfigAndVsyncPeriod);
         return true;
     }
 
+    static void hotplugHook(hwc2_callback_data_t callbackData, hwc2_display_t display,
+                            int32_t connected) {
+        auto hal = static_cast<HwcHalImpl*>(callbackData);
+        hal->mEventCallback_2_4->onHotplug(display,
+                                           static_cast<IComposerCallback::Connection>(connected));
+    }
+
+    static void refreshHook(hwc2_callback_data_t callbackData, hwc2_display_t display) {
+        auto hal = static_cast<HwcHalImpl*>(callbackData);
+        hal->mEventCallback_2_4->onRefresh(display);
+    }
+
+    static void vsyncHook(hwc2_callback_data_t callbackData, hwc2_display_t display,
+                          int64_t timestamp) {
+        auto hal = static_cast<HwcHalImpl*>(callbackData);
+        hal->mEventCallback_2_4->onVsync(display, timestamp);
+    }
+
+    static void vsync_2_4_Hook(hwc2_callback_data_t callbackData, hwc2_display_t display,
+                               int64_t timestamp, hwc2_vsync_period_t vsyncPeriodNanos) {
+        auto hal = static_cast<HwcHalImpl*>(callbackData);
+        hal->mEventCallback_2_4->onVsync_2_4(display, timestamp, vsyncPeriodNanos);
+    }
+
   private:
     struct {
         HWC2_PFN_GET_DISPLAY_CONNECTION_TYPE getDisplayConnectionType;
+        HWC2_PFN_REGISTER_CALLBACK registerCallback;
+        HWC2_PFN_GET_SUPPORTED_DISPLAY_VSYNC_PERIODS getSupportedDisplayVsyncPeriods;
+        HWC2_PFN_GET_DISPLAY_VSYNC_PERIOD getDisplayVsyncPeriod;
+        HWC2_PFN_SET_ACTIVE_CONFIG_AND_VSYNC_PERIOD setActiveConfigAndVsyncPeriod;
     } mDispatch = {};
 
+    hal::ComposerHal::EventCallback_2_4* mEventCallback_2_4 = nullptr;
+
     using BaseType2_1 = V2_1::passthrough::detail::HwcHalImpl<Hal>;
     using BaseType2_3 = V2_3::passthrough::detail::HwcHalImpl<Hal>;
     using BaseType2_1::mDevice;
diff --git a/graphics/composer/2.4/utils/vts/ComposerVts.cpp b/graphics/composer/2.4/utils/vts/ComposerVts.cpp
index 937b50e..b02a59a 100644
--- a/graphics/composer/2.4/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.4/utils/vts/ComposerVts.cpp
@@ -25,7 +25,7 @@
 namespace V2_4 {
 namespace vts {
 
-using V2_1::Error;
+using V2_4::Error;
 
 Composer::Composer() : Composer(::testing::VtsHalHidlTargetTestBase::getService<IComposer>()) {}
 
@@ -70,6 +70,40 @@
     return error;
 }
 
+Error ComposerClient::getSupportedDisplayVsyncPeriods(
+        Display display, Config config, std::vector<VsyncPeriodNanos>* outSupportedVsyncPeriods) {
+    Error error = Error::NONE;
+    mClient->getSupportedDisplayVsyncPeriods(
+            display, config, [&](const auto& tmpError, const auto& tmpSupportedVsyncPeriods) {
+                error = tmpError;
+                *outSupportedVsyncPeriods = tmpSupportedVsyncPeriods;
+            });
+    return error;
+}
+
+Error ComposerClient::getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) {
+    Error error = Error::NONE;
+    mClient->getDisplayVsyncPeriod(display, [&](const auto& tmpError, const auto& tmpVsyncPeriod) {
+        error = tmpError;
+        *outVsyncPeriod = tmpVsyncPeriod;
+    });
+    return error;
+}
+
+Error ComposerClient::setActiveConfigAndVsyncPeriod(
+        Display display, Config config, VsyncPeriodNanos vsyncPeriodNanos,
+        const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+        int64_t* outNewVsyncAppliedTime) {
+    Error error = Error::NONE;
+    mClient->setActiveConfigAndVsyncPeriod(
+            display, config, vsyncPeriodNanos, vsyncPeriodChangeConstraints,
+            [&](const auto& tmpError, const auto& tmpNewVsyncAppliedTime) {
+                error = tmpError;
+                *outNewVsyncAppliedTime = tmpNewVsyncAppliedTime;
+            });
+    return error;
+}
+
 }  // namespace vts
 }  // namespace V2_4
 }  // namespace composer
diff --git a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
index a7d7f86..e8a3905 100644
--- a/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
+++ b/graphics/composer/2.4/utils/vts/include/composer-vts/2.4/ComposerVts.h
@@ -37,10 +37,12 @@
 using common::V1_2::Dataspace;
 using common::V1_2::Hdr;
 using common::V1_2::PixelFormat;
+using V2_1::Config;
 using V2_1::Display;
-using V2_1::Error;
+using V2_4::Error;
 using V2_4::IComposer;
 using V2_4::IComposerClient;
+using V2_4::VsyncPeriodNanos;
 
 class ComposerClient;
 
@@ -74,6 +76,16 @@
     Error getDisplayConnectionType(Display display,
                                    IComposerClient::DisplayConnectionType* outType);
 
+    Error getSupportedDisplayVsyncPeriods(Display display, Config config,
+                                          std::vector<VsyncPeriodNanos>* outSupportedVsyncPeriods);
+
+    Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriods);
+
+    Error setActiveConfigAndVsyncPeriod(
+            Display display, Config config, VsyncPeriodNanos vsyncPeriodNanos,
+            const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+            int64_t* outNewVsyncAppliedTime);
+
   private:
     const sp<IComposerClient> mClient;
 };
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index 76c0039..3d967fc 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "graphics_composer_hidl_hal_test@2.4"
 
 #include <algorithm>
+#include <thread>
 
 #include <VtsHalHidlTargetTestBase.h>
 #include <android-base/logging.h>
@@ -26,6 +27,7 @@
 #include <composer-vts/2.1/TestCommandReader.h>
 #include <composer-vts/2.4/ComposerVts.h>
 #include <mapper-vts/2.0/MapperVts.h>
+#include <utils/Timers.h>
 
 namespace android {
 namespace hardware {
@@ -110,8 +112,27 @@
         return 0;
     }
 
+    // returns an invalid config id (one that has not been registered to a
+    // display).  Currently assuming that a device will never have close to
+    // std::numeric_limit<uint64_t>::max() configs registered while running tests
+    Display GetInvalidConfigId(Display display) {
+        std::vector<Config> validConfigs = mComposerClient->getDisplayConfigs(display);
+        uint64_t id = std::numeric_limits<uint64_t>::max();
+        while (id > 0) {
+            if (std::find(validConfigs.begin(), validConfigs.end(), id) == validConfigs.end()) {
+                return id;
+            }
+            id--;
+        }
+
+        return 0;
+    }
+
     void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); }
 
+    void Test_setActiveConfigAndVsyncPeriod(
+            const IComposerClient::VsyncPeriodChangeConstraints& constraints);
+
     std::unique_ptr<Composer> mComposer;
     std::unique_ptr<ComposerClient> mComposerClient;
     sp<V2_1::vts::GraphicsComposerCallback> mComposerCallback;
@@ -189,6 +210,162 @@
     }
 }
 
+TEST_F(GraphicsComposerHidlTest, getSupportedDisplayVsyncPeriods_BadDisplay) {
+    std::vector<VsyncPeriodNanos> supportedVsyncPeriods;
+    EXPECT_EQ(Error::BAD_DISPLAY, mComposerClient->getSupportedDisplayVsyncPeriods(
+                                          mInvalidDisplayId, Config(0), &supportedVsyncPeriods));
+}
+
+TEST_F(GraphicsComposerHidlTest, getSupportedDisplayVsyncPeriods_BadConfig) {
+    for (Display display : mComposerCallback->getDisplays()) {
+        Config invalidConfigId = GetInvalidConfigId(display);
+        std::vector<VsyncPeriodNanos> supportedVsyncPeriods;
+        EXPECT_EQ(Error::BAD_CONFIG, mComposerClient->getSupportedDisplayVsyncPeriods(
+                                             display, invalidConfigId, &supportedVsyncPeriods));
+    }
+}
+
+TEST_F(GraphicsComposerHidlTest, getSupportedDisplayVsyncPeriods) {
+    for (Display display : mComposerCallback->getDisplays()) {
+        for (Config config : mComposerClient->getDisplayConfigs(display)) {
+            std::vector<VsyncPeriodNanos> supportedVsyncPeriods;
+
+            // Get the default vsync period from the config
+            VsyncPeriodNanos defaultVsyncPeiord = mComposerClient->getDisplayAttribute(
+                    display, config, IComposerClient::Attribute::VSYNC_PERIOD);
+            // Get all supported vsync periods for this config
+            EXPECT_EQ(Error::NONE, mComposerClient->getSupportedDisplayVsyncPeriods(
+                                           display, config, &supportedVsyncPeriods));
+            // Default vsync period must be present in the list
+            EXPECT_NE(std::find(supportedVsyncPeriods.begin(), supportedVsyncPeriods.end(),
+                                defaultVsyncPeiord),
+                      supportedVsyncPeriods.end());
+
+            // Each vsync period must be unique
+            std::unordered_set<VsyncPeriodNanos> vsyncPeriodSet;
+            for (VsyncPeriodNanos vsyncPeriodNanos : supportedVsyncPeriods) {
+                EXPECT_TRUE(vsyncPeriodSet.insert(vsyncPeriodNanos).second);
+            }
+        }
+    }
+}
+
+TEST_F(GraphicsComposerHidlTest, getDisplayVsyncPeriod_BadDisplay) {
+    VsyncPeriodNanos vsyncPeriodNanos;
+    EXPECT_EQ(Error::BAD_DISPLAY,
+              mComposerClient->getDisplayVsyncPeriod(mInvalidDisplayId, &vsyncPeriodNanos));
+}
+
+TEST_F(GraphicsComposerHidlTest, setActiveConfigAndVsyncPeriod_BadDisplay) {
+    int64_t newVsyncAppliedTime;
+    IComposerClient::VsyncPeriodChangeConstraints constraints;
+
+    constraints.seamlessRequired = false;
+    constraints.desiredTimeNanos = systemTime();
+
+    EXPECT_EQ(Error::BAD_DISPLAY, mComposerClient->setActiveConfigAndVsyncPeriod(
+                                          mInvalidDisplayId, Config(0), VsyncPeriodNanos(0),
+                                          constraints, &newVsyncAppliedTime));
+}
+
+TEST_F(GraphicsComposerHidlTest, setActiveConfigAndVsyncPeriod_BadConfig) {
+    int64_t newVsyncAppliedTime;
+    IComposerClient::VsyncPeriodChangeConstraints constraints;
+
+    constraints.seamlessRequired = false;
+    constraints.desiredTimeNanos = systemTime();
+
+    for (Display display : mComposerCallback->getDisplays()) {
+        Config invalidConfigId = GetInvalidConfigId(display);
+        EXPECT_EQ(Error::BAD_CONFIG, mComposerClient->setActiveConfigAndVsyncPeriod(
+                                             display, invalidConfigId, VsyncPeriodNanos(0),
+                                             constraints, &newVsyncAppliedTime));
+    }
+}
+
+TEST_F(GraphicsComposerHidlTest, setActiveConfigAndVsyncPeriod_BadVsyncPeriod) {
+    int64_t newVsyncAppliedTime;
+    IComposerClient::VsyncPeriodChangeConstraints constraints;
+
+    constraints.seamlessRequired = false;
+    constraints.desiredTimeNanos = systemTime();
+
+    for (Display display : mComposerCallback->getDisplays()) {
+        for (Config config : mComposerClient->getDisplayConfigs(display)) {
+            EXPECT_EQ(Error::BAD_VSYNC_PERIOD, mComposerClient->setActiveConfigAndVsyncPeriod(
+                                                       display, config, VsyncPeriodNanos(0),
+                                                       constraints, &newVsyncAppliedTime));
+        }
+    }
+}
+
+void GraphicsComposerHidlTest::Test_setActiveConfigAndVsyncPeriod(
+        const IComposerClient::VsyncPeriodChangeConstraints& constraints) {
+    int64_t newVsyncAppliedTime;
+
+    for (Display display : mComposerCallback->getDisplays()) {
+        for (Config config : mComposerClient->getDisplayConfigs(display)) {
+            std::vector<VsyncPeriodNanos> supportedVsyncPeriods;
+
+            EXPECT_EQ(Error::NONE, mComposerClient->getSupportedDisplayVsyncPeriods(
+                                           display, config, &supportedVsyncPeriods));
+            for (VsyncPeriodNanos newVsyncPeriod : supportedVsyncPeriods) {
+                VsyncPeriodNanos vsyncPeriodNanos;
+                EXPECT_EQ(Error::NONE,
+                          mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
+
+                if (vsyncPeriodNanos == newVsyncPeriod) {
+                    continue;
+                }
+
+                EXPECT_EQ(Error::NONE, mComposerClient->setActiveConfigAndVsyncPeriod(
+                                               display, config, newVsyncPeriod, constraints,
+                                               &newVsyncAppliedTime));
+
+                EXPECT_TRUE(newVsyncAppliedTime >= constraints.desiredTimeNanos);
+
+                // Refresh rate should change within a reasonable time
+                constexpr nsecs_t kReasonableTimeForChange = 1'000'000'000;  // 1 second
+                EXPECT_TRUE(newVsyncAppliedTime - constraints.desiredTimeNanos <=
+                            kReasonableTimeForChange);
+
+                while (systemTime() <= newVsyncAppliedTime) {
+                    EXPECT_EQ(Error::NONE,
+                              mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
+                    if (systemTime() <= constraints.desiredTimeNanos) {
+                        EXPECT_NE(vsyncPeriodNanos, newVsyncPeriod);
+                    }
+
+                    if (vsyncPeriodNanos == newVsyncPeriod) {
+                        break;
+                    }
+                    std::this_thread::sleep_for(10ms);
+                }
+                EXPECT_EQ(Error::NONE,
+                          mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
+                EXPECT_EQ(vsyncPeriodNanos, newVsyncPeriod);
+            }
+        }
+    }
+}
+
+TEST_F(GraphicsComposerHidlTest, setActiveConfigAndVsyncPeriod) {
+    IComposerClient::VsyncPeriodChangeConstraints constraints;
+
+    constraints.seamlessRequired = false;
+    constraints.desiredTimeNanos = systemTime();
+    Test_setActiveConfigAndVsyncPeriod(constraints);
+}
+
+TEST_F(GraphicsComposerHidlTest, setActiveConfigAndVsyncPeriod_delayed) {
+    IComposerClient::VsyncPeriodChangeConstraints constraints;
+
+    constexpr auto kDelayForChange = 300ms;
+    constraints.seamlessRequired = false;
+    constraints.desiredTimeNanos = systemTime() + kDelayForChange.count();
+    Test_setActiveConfigAndVsyncPeriod(constraints);
+}
+
 }  // namespace
 }  // namespace vts
 }  // namespace V2_4