Merge "Add compatibility param to setFrameRate() api"
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index eeb8330..c30dcfe 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -425,12 +425,15 @@
  * valid refresh rate for this device's display - e.g., it's fine to pass 30fps to a device that can
  * only run the display at 60fps.
  *
+ * |compatibility| The frame rate compatibility of this surface. The compatibility value may
+ * influence the system's choice of display frame rate. To specify a compatibility use the
+ * ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* enum.
+ *
  * Available since API level 30.
  */
 void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction,
-                                      ASurfaceControl* surface_control,
-                                      float frameRate)
-                                      __INTRODUCED_IN(30);
+                                      ASurfaceControl* surface_control, float frameRate,
+                                      int8_t compatibility) __INTRODUCED_IN(30);
 
 #endif // __ANDROID_API__ >= 30
 
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 2f27fd2..ce41eab 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -1112,6 +1112,42 @@
         }
         return NO_ERROR;
     }
+
+    virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
+                                  int8_t compatibility) {
+        Parcel data, reply;
+        status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+        if (err != NO_ERROR) {
+            ALOGE("setFrameRate: failed writing interface token: %s (%d)", strerror(-err), -err);
+            return err;
+        }
+
+        err = data.writeStrongBinder(IInterface::asBinder(surface));
+        if (err != NO_ERROR) {
+            ALOGE("setFrameRate: failed writing strong binder: %s (%d)", strerror(-err), -err);
+            return err;
+        }
+
+        err = data.writeFloat(frameRate);
+        if (err != NO_ERROR) {
+            ALOGE("setFrameRate: failed writing float: %s (%d)", strerror(-err), -err);
+            return err;
+        }
+
+        err = data.writeByte(compatibility);
+        if (err != NO_ERROR) {
+            ALOGE("setFrameRate: failed writing byte: %s (%d)", strerror(-err), -err);
+            return err;
+        }
+
+        err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply,
+                                 IBinder::FLAG_ONEWAY);
+        if (err != NO_ERROR) {
+            ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err);
+            return err;
+        }
+        return NO_ERROR;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -1877,6 +1913,36 @@
             return setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ,
                                            lightRadius);
         }
+        case SET_FRAME_RATE: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<IBinder> binder;
+            status_t err = data.readStrongBinder(&binder);
+            if (err != NO_ERROR) {
+                ALOGE("setFrameRate: failed to read strong binder: %s (%d)", strerror(-err), -err);
+                return err;
+            }
+            sp<IGraphicBufferProducer> surface = interface_cast<IGraphicBufferProducer>(binder);
+            if (!surface) {
+                ALOGE("setFrameRate: failed to cast to IGraphicBufferProducer: %s (%d)",
+                      strerror(-err), -err);
+                return err;
+            }
+            float frameRate;
+            err = data.readFloat(&frameRate);
+            if (err != NO_ERROR) {
+                ALOGE("setFrameRate: failed to read float: %s (%d)", strerror(-err), -err);
+                return err;
+            }
+            int8_t compatibility;
+            err = data.readByte(&compatibility);
+            if (err != NO_ERROR) {
+                ALOGE("setFrameRate: failed to read byte: %s (%d)", strerror(-err), -err);
+                return err;
+            }
+            status_t result = setFrameRate(surface, frameRate, compatibility);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
         default: {
             return BBinder::onTransact(code, data, reply, flags);
         }
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 5547efc..a9c9b74 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -24,6 +24,8 @@
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/LayerState.h>
 
+#include <cmath>
+
 namespace android {
 
 status_t layer_state_t::write(Parcel& output) const
@@ -113,6 +115,7 @@
     output.writeFloat(shadowRadius);
     output.writeInt32(frameRateSelectionPriority);
     output.writeFloat(frameRate);
+    output.writeByte(frameRateCompatibility);
     return NO_ERROR;
 }
 
@@ -194,6 +197,7 @@
     shadowRadius = input.readFloat();
     frameRateSelectionPriority = input.readInt32();
     frameRate = input.readFloat();
+    frameRateCompatibility = input.readByte();
     return NO_ERROR;
 }
 
@@ -427,6 +431,7 @@
     if (other.what & eFrameRateChanged) {
         what |= eFrameRateChanged;
         frameRate = other.frameRate;
+        frameRateCompatibility = other.frameRateCompatibility;
     }
     if ((other.what & what) != other.what) {
         ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
@@ -474,4 +479,21 @@
     syncInputWindows = input.readBool();
 }
 
+bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunctionName) {
+    const char* functionName = inFunctionName != nullptr ? inFunctionName : "call";
+    int floatClassification = std::fpclassify(frameRate);
+    if (frameRate < 0 || floatClassification == FP_INFINITE || floatClassification == FP_NAN) {
+        ALOGE("%s failed - invalid frame rate %f", functionName, frameRate);
+        return false;
+    }
+
+    if (compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT &&
+        compatibility != ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE) {
+        ALOGE("%s failed - invalid compatibility value %d", functionName, compatibility);
+        return false;
+    }
+
+    return true;
+}
+
 }; // namespace android
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 278cc59..f911e70 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -43,6 +43,7 @@
 #include <gui/IProducerListener.h>
 
 #include <gui/ISurfaceComposer.h>
+#include <gui/LayerState.h>
 #include <private/gui/ComposerService.h>
 
 namespace android {
@@ -1413,7 +1414,8 @@
 
 int Surface::dispatchSetFrameRate(va_list args) {
     float frameRate = static_cast<float>(va_arg(args, double));
-    return setFrameRate(frameRate);
+    int8_t compatibility = static_cast<int8_t>(va_arg(args, int));
+    return setFrameRate(frameRate, compatibility);
 }
 
 int Surface::dispatchAddCancelInterceptor(va_list args) {
@@ -2222,11 +2224,15 @@
     mSurfaceListener->onBuffersDiscarded(discardedBufs);
 }
 
-status_t Surface::setFrameRate(float frameRate) {
+status_t Surface::setFrameRate(float frameRate, int8_t compatibility) {
     ATRACE_CALL();
-    ALOGV("Surface::setTargetFrameRate");
-    Mutex::Autolock lock(mMutex);
-    return mGraphicBufferProducer->setFrameRate(frameRate);
+    ALOGV("Surface::setFrameRate");
+
+    if (!ValidateFrameRate(frameRate, compatibility, "Surface::setFrameRate")) {
+        return BAD_VALUE;
+    }
+
+    return composerService()->setFrameRate(mGraphicBufferProducer, frameRate, compatibility);
 }
 
 }; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index ff8b719..dc4860a 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1387,14 +1387,19 @@
 }
 
 SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate(
-        const sp<SurfaceControl>& sc, float frameRate) {
+        const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility) {
     layer_state_t* s = getLayerState(sc);
     if (!s) {
         mStatus = BAD_INDEX;
         return *this;
     }
+    if (!ValidateFrameRate(frameRate, compatibility, "Transaction::setFrameRate")) {
+        mStatus = BAD_VALUE;
+        return *this;
+    }
     s->what |= layer_state_t::eFrameRateChanged;
     s->frameRate = frameRate;
+    s->frameRateCompatibility = compatibility;
     return *this;
 }
 
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index e860f61..0659f0d 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -500,6 +500,12 @@
     virtual status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
                                              float lightPosY, float lightPosZ,
                                              float lightRadius) = 0;
+
+    /*
+     * Sets the intended frame rate for a surface. See ANativeWindow_setFrameRate() for more info.
+     */
+    virtual status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
+                                  int8_t compatibility) = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -557,6 +563,7 @@
         SET_AUTO_LOW_LATENCY_MODE,
         GET_GAME_CONTENT_TYPE_SUPPORT,
         SET_GAME_CONTENT_TYPE,
+        SET_FRAME_RATE,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 2d53b48..7e3d5d5 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -20,8 +20,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <utils/Errors.h>
-
+#include <android/native_window.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/ITransactionCompletedListener.h>
 #include <math/mat4.h>
@@ -36,6 +35,7 @@
 #include <ui/Rect.h>
 #include <ui/Region.h>
 #include <ui/Rotation.h>
+#include <utils/Errors.h>
 
 namespace android {
 
@@ -135,7 +135,8 @@
             colorSpaceAgnostic(false),
             shadowRadius(0.0f),
             frameRateSelectionPriority(-1),
-            frameRate(0.0f) {
+            frameRate(0.0f),
+            frameRateCompatibility(ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT) {
         matrix.dsdx = matrix.dtdy = 1.0f;
         matrix.dsdy = matrix.dtdx = 0.0f;
         hdrMetadata.validTypes = 0;
@@ -221,7 +222,9 @@
     // Priority of the layer assigned by Window Manager.
     int32_t frameRateSelectionPriority;
 
+    // Layer frame rate and compatibility. See ANativeWindow_setFrameRate().
     float frameRate;
+    int8_t frameRateCompatibility;
 };
 
 struct ComposerState {
@@ -292,6 +295,12 @@
     return compare_type(lhs.token, rhs.token);
 }
 
+// Returns true if the frameRate and compatibility are valid values, false
+// othwerise. If either of the params are invalid, an error log is printed, and
+// functionName is added to the log to indicate which function call failed.
+// functionName can be null.
+bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* functionName);
+
 }; // namespace android
 
 #endif // ANDROID_SF_LAYER_STATE_H
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 4a353fc..ad7cbfe 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -179,7 +179,7 @@
     status_t getConsumerUsage(uint64_t* outUsage) const;
 
     // See IGraphicBufferProducer::setFrameRate
-    status_t setFrameRate(float frameRate);
+    status_t setFrameRate(float frameRate, int8_t compatibility);
 
 protected:
     virtual ~Surface();
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 27877bb..0cf141d 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -519,7 +519,8 @@
                 const Rect& source, const Rect& dst, int transform);
         Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius);
 
-        Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate);
+        Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
+                                  int8_t compatibility);
 
         status_t setDisplaySurface(const sp<IBinder>& token,
                 const sp<IGraphicBufferProducer>& bufferProducer);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 70fd888..8c0f8f8 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -854,6 +854,11 @@
         return NO_ERROR;
     }
 
+    status_t setFrameRate(const sp<IGraphicBufferProducer>& /*surface*/, float /*frameRate*/,
+                          int8_t /*compatibility*/) override {
+        return NO_ERROR;
+    }
+
 protected:
     IBinder* onAsBinder() override { return nullptr; }
 
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 98b76fd..f09decf 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -158,11 +158,11 @@
     return query(window, NATIVE_WINDOW_DATASPACE);
 }
 
-int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate) {
-    if (!window || !query(window, NATIVE_WINDOW_IS_VALID) || frameRate < 0) {
+int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility) {
+    if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) {
         return -EINVAL;
     }
-    return native_window_set_frame_rate(window, frameRate);
+    return native_window_set_frame_rate(window, frameRate, compatibility);
 }
 
 void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) {
diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h
index 4b426c5..59aa665 100644
--- a/libs/nativewindow/include/android/native_window.h
+++ b/libs/nativewindow/include/android/native_window.h
@@ -33,6 +33,7 @@
 #ifndef ANDROID_NATIVE_WINDOW_H
 #define ANDROID_NATIVE_WINDOW_H
 
+#include <stdint.h>
 #include <sys/cdefs.h>
 
 #include <android/data_space.h>
@@ -232,6 +233,24 @@
 
 #if __ANDROID_API__ >= 30
 
+/* Parameter for ANativeWindow_setFrameRate */
+enum {
+    /**
+     * There are no inherent restrictions on the frame rate of this window.
+     */
+    ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT = 0,
+    /**
+     * This window is being used to display content with an inherently fixed
+     * frame rate, e.g. a video that has a specific frame rate. When the system
+     * selects a frame rate other than what the app requested, the app will need
+     * to do pull down or use some other technique to adapt to the system's
+     * frame rate. The user experience is likely to be worse (e.g. more frame
+     * stuttering) than it would be if the system had chosen the app's requested
+     * frame rate.
+     */
+    ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1
+};
+
 /**
  * Sets the intended frame rate for this window.
  *
@@ -257,9 +276,15 @@
  * refresh rate for this device's display - e.g., it's fine to pass 30fps to a
  * device that can only run the display at 60fps.
  *
- * \return 0 for success, -EINVAL if the window or frame rate are invalid.
+ * \param compatibility The frame rate compatibility of this window. The
+ * compatibility value may influence the system's choice of display refresh
+ * rate. See the ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* values for more info.
+ *
+ * \return 0 for success, -EINVAL if the window, frame rate, or compatibility
+ * value are invalid.
  */
-int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate) __INTRODUCED_IN(30);
+int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_t compatibility)
+        __INTRODUCED_IN(30);
 
 /**
  * Provides a hint to the window that buffers should be preallocated ahead of
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index f686147..0e28fb8 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1015,8 +1015,10 @@
     return window->perform(window, NATIVE_WINDOW_SET_AUTO_PREROTATION, autoPrerotation);
 }
 
-static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate) {
-    return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate);
+static inline int native_window_set_frame_rate(struct ANativeWindow* window, float frameRate,
+                                               int8_t compatibility) {
+    return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, (double)frameRate,
+                           (int)compatibility);
 }
 
 // ------------------------------------------------------------------------------------------------
diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt
index 154eb8e..e072e11 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -46,9 +46,9 @@
     ANativeWindow_setBuffersTimestamp; # llndk
     ANativeWindow_setBuffersTransform;
     ANativeWindow_setDequeueTimeout; # apex # introduced=30
+    ANativeWindow_setFrameRate; # introduced=30
     ANativeWindow_setSharedBufferMode; # llndk
     ANativeWindow_setSwapInterval; # llndk
-    ANativeWindow_setFrameRate; # introduced=30
     ANativeWindow_setUsage; # llndk
     ANativeWindow_tryAllocateBuffers; # introduced=30
     ANativeWindow_unlockAndPost;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index e65064b..f5a99ca 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -124,18 +124,6 @@
     return isDue || !isPlausible;
 }
 
-bool BufferQueueLayer::setFrameRate(FrameRate frameRate) {
-    float oldFrameRate = 0.f;
-    status_t result = mConsumer->getFrameRate(&oldFrameRate);
-    bool frameRateChanged = result < 0 || frameRate.rate != oldFrameRate;
-    mConsumer->setFrameRate(frameRate.rate);
-    return frameRateChanged;
-}
-
-Layer::FrameRate BufferQueueLayer::getFrameRate() const {
-    return FrameRate(mLatchedFrameRate, Layer::FrameRateCompatibility::Default);
-}
-
 // -----------------------------------------------------------------------
 // Interface implementation for BufferLayer
 // -----------------------------------------------------------------------
@@ -578,7 +566,6 @@
     mBufferInfo.mTransformToDisplayInverse = mConsumer->getTransformToDisplayInverse();
     float latchedFrameRate;
     mConsumer->getFrameRate(&latchedFrameRate);
-    mLatchedFrameRate = latchedFrameRate;
 }
 
 sp<Layer> BufferQueueLayer::createClone() {
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 626af4b..5f7587c 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -56,9 +56,6 @@
 
     bool shouldPresentNow(nsecs_t expectedPresentTime) const override;
 
-    bool setFrameRate(FrameRate frameRate) override;
-    FrameRate getFrameRate() const override;
-
     // -----------------------------------------------------------------------
 
     // -----------------------------------------------------------------------
@@ -155,8 +152,6 @@
     std::atomic<bool> mSidebandStreamChanged{false};
 
     sp<ContentsChangedListener> mContentsChangedListener;
-
-    std::atomic<float> mLatchedFrameRate = 0.f;
 };
 
 } // namespace android
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index da26a37..d7647d7 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -26,6 +26,7 @@
 #include "Layer.h"
 
 #include <android-base/stringprintf.h>
+#include <android/native_window.h>
 #include <binder/IPCThreadState.h>
 #include <compositionengine/Display.h>
 #include <compositionengine/LayerFECompositionState.h>
@@ -2446,6 +2447,18 @@
     layer->mDrawingParent = this;
 }
 
+Layer::FrameRateCompatibility Layer::FrameRate::convertCompatibility(int8_t compatibility) {
+    switch (compatibility) {
+        case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT:
+            return FrameRateCompatibility::Default;
+        case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE:
+            return FrameRateCompatibility::ExactOrMultiple;
+        default:
+            LOG_ALWAYS_FATAL("Invalid frame rate compatibility value %d", compatibility);
+            return FrameRateCompatibility::Default;
+    }
+}
+
 // ---------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 37ae340..5d2144a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -161,6 +161,10 @@
         }
 
         bool operator!=(const FrameRate& other) const { return !(*this == other); }
+
+        // Convert an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value to a
+        // Layer::FrameRateCompatibility. Logs fatal if the compatibility value is invalid.
+        static FrameRateCompatibility convertCompatibility(int8_t compatibility);
     };
 
     struct State {
@@ -795,7 +799,7 @@
      */
     Rect getCroppedBufferSize(const Layer::State& s) const;
 
-    virtual bool setFrameRate(FrameRate frameRate);
+    bool setFrameRate(FrameRate frameRate);
     virtual FrameRate getFrameRate() const;
 
 protected:
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index bc0111b..a8e6756 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -180,4 +180,3 @@
     mActiveLayersEnd = 0;
 }
 } // namespace android::scheduler::impl
-
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index dcbd934..fbebea0 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -34,6 +34,8 @@
 #include <optional>
 #include <unordered_map>
 
+#include <android/native_window.h>
+
 #include <cutils/properties.h>
 #include <log/log.h>
 
@@ -58,6 +60,7 @@
 #include <gui/IProducerListener.h>
 #include <gui/LayerDebugInfo.h>
 #include <gui/LayerMetadata.h>
+#include <gui/LayerState.h>
 #include <gui/Surface.h>
 #include <input/IInputFlinger.h>
 #include <renderengine/RenderEngine.h>
@@ -3599,9 +3602,13 @@
         }
     }
     if (what & layer_state_t::eFrameRateChanged) {
-        if (layer->setFrameRate(
-                    Layer::FrameRate(s.frameRate, Layer::FrameRateCompatibility::Default)))
+        if (ValidateFrameRate(s.frameRate, s.frameRateCompatibility,
+                              "SurfaceFlinger::setClientStateLocked") &&
+            layer->setFrameRate(Layer::FrameRate(s.frameRate,
+                                                 Layer::FrameRate::convertCompatibility(
+                                                         s.frameRateCompatibility)))) {
             flags |= eTraversalNeeded;
+        }
     }
     // This has to happen after we reparent children because when we reparent to null we remove
     // child layers from current state and remove its relative z. If the children are reparented in
@@ -4699,6 +4706,9 @@
         case GET_COMPOSITION_PREFERENCE:
         case GET_PROTECTED_CONTENT_SUPPORT:
         case IS_WIDE_COLOR_DISPLAY:
+        // setFrameRate() is deliberately available for apps to call without any
+        // special permissions.
+        case SET_FRAME_RATE:
         case GET_DISPLAY_BRIGHTNESS_SUPPORT:
         case SET_DISPLAY_BRIGHTNESS: {
             return OK;
@@ -5912,6 +5922,27 @@
     return genericLayerMetadataKeyMap;
 }
 
+status_t SurfaceFlinger::setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
+                                      int8_t compatibility) {
+    if (!ValidateFrameRate(frameRate, compatibility, "SurfaceFlinger::setFrameRate")) {
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mStateLock);
+    if (authenticateSurfaceTextureLocked(surface)) {
+        sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
+        if (layer->setFrameRate(
+                    Layer::FrameRate(frameRate,
+                                     Layer::FrameRate::convertCompatibility(compatibility)))) {
+            setTransactionFlags(eTraversalNeeded);
+        }
+    } else {
+        ALOGE("Attempt to set frame rate on an unrecognized IGraphicBufferProducer");
+        return BAD_VALUE;
+    }
+    return NO_ERROR;
+}
+
 } // namespace android
 
 #if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 6770c1c..dcbb150 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -493,6 +493,8 @@
     status_t notifyPowerHint(int32_t hintId) override;
     status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
                                      float lightPosY, float lightPosZ, float lightRadius) override;
+    status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
+                          int8_t compatibility) override;
     /* ------------------------------------------------------------------------
      * DeathRecipient interface
      */
diff --git a/services/surfaceflinger/tests/SetFrameRate_test.cpp b/services/surfaceflinger/tests/SetFrameRate_test.cpp
index fc65263..02ba9e2 100644
--- a/services/surfaceflinger/tests/SetFrameRate_test.cpp
+++ b/services/surfaceflinger/tests/SetFrameRate_test.cpp
@@ -58,17 +58,23 @@
 
 TEST_F(SetFrameRateTest, BufferQueueLayerSetFrameRate) {
     CreateLayer(ISurfaceComposerClient::eFXSurfaceBufferQueue);
-    native_window_set_frame_rate(mLayer->getSurface().get(), 100.f);
+    native_window_set_frame_rate(mLayer->getSurface().get(), 100.f,
+                                 ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT);
     ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
-    Transaction().setFrameRate(mLayer, 200.f).apply();
+    Transaction()
+            .setFrameRate(mLayer, 200.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT)
+            .apply();
     ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
-    native_window_set_frame_rate(mLayer->getSurface().get(), 300.f);
+    native_window_set_frame_rate(mLayer->getSurface().get(), 300.f,
+                                 ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT);
     ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::RED));
 }
 
 TEST_F(SetFrameRateTest, BufferStateLayerSetFrameRate) {
     CreateLayer(ISurfaceComposerClient::eFXSurfaceBufferState);
-    Transaction().setFrameRate(mLayer, 400.f).apply();
+    Transaction()
+            .setFrameRate(mLayer, 400.f, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT)
+            .apply();
     ASSERT_NO_FATAL_FAILURE(PostBuffers(Color::GREEN));
 }