Add setFrameRate() api

setFrameRate() is a new api in Android 11 that will enable apps to
specify their intended frame rate.

Bug: 143912624
Bug: 137287430

Test: Added a new CTS test - android.graphics.cts.SetFrameRateTest.

Change-Id: I922573c0d704e2e0ce4cfc2a462f14dce8cb7a79
diff --git a/api/current.txt b/api/current.txt
index c4ac833..c78f090 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -52446,6 +52446,7 @@
     method public android.graphics.Canvas lockHardwareCanvas();
     method public void readFromParcel(android.os.Parcel);
     method public void release();
+    method public void setFrameRate(@FloatRange(from=0.0) float);
     method @Deprecated public void unlockCanvas(android.graphics.Canvas);
     method public void unlockCanvasAndPost(android.graphics.Canvas);
     method public void writeToParcel(android.os.Parcel, int);
@@ -52489,6 +52490,7 @@
     method @NonNull public android.view.SurfaceControl.Transaction reparent(@NonNull android.view.SurfaceControl, @Nullable android.view.SurfaceControl);
     method @NonNull public android.view.SurfaceControl.Transaction setAlpha(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0, to=1.0) float);
     method @NonNull public android.view.SurfaceControl.Transaction setBufferSize(@NonNull android.view.SurfaceControl, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float);
     method @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int);
     method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
     method @NonNull public android.view.SurfaceControl.Transaction setVisibility(@NonNull android.view.SurfaceControl, boolean);
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 7707ad1..a6b7c33 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -88,6 +89,8 @@
     private static native int nativeSetSharedBufferModeEnabled(long nativeObject, boolean enabled);
     private static native int nativeSetAutoRefreshEnabled(long nativeObject, boolean enabled);
 
+    private static native int nativeSetFrameRate(long nativeObject, float frameRate);
+
     public static final @android.annotation.NonNull Parcelable.Creator<Surface> CREATOR =
             new Parcelable.Creator<Surface>() {
         @Override
@@ -841,6 +844,34 @@
     }
 
     /**
+     * Sets the intended frame rate for this surface.
+     *
+     * On devices that are capable of running the display at different refresh rates, the
+     * system may choose a display refresh rate to better match this surface's frame
+     * rate. Usage of this API won't introduce frame rate throttling, or affect other
+     * aspects of the application's frame production pipeline. However, because the system
+     * may change the display refresh rate, calls to this function may result in changes
+     * to Choreographer callback timings, and changes to the time interval at which the
+     * system releases buffers back to the application.
+     *
+     * Note that this only has an effect for surfaces presented on the display. If this
+     * surface is consumed by something other than the system compositor, e.g. a media
+     * codec, this call has no effect.
+     *
+     * @param frameRate The intended frame rate of this surface. 0 is a special value that
+     * indicates the app will accept the system's choice for the display frame rate, which
+     * is the default behavior if this function isn't called. The frameRate param does
+     * *not* need to be a 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.
+     */
+    public void setFrameRate(@FloatRange(from = 0.0) float frameRate) {
+        int error = nativeSetFrameRate(mNativeObject, frameRate);
+        if (error != 0) {
+            throw new RuntimeException("Failed to set frame rate on Surface");
+        }
+    }
+
+    /**
      * Exception thrown when a Canvas couldn't be locked with {@link Surface#lockCanvas}, or
      * when a SurfaceTexture could not successfully be allocated.
      */
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index bcc9e41..f7b87cc 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -211,6 +211,9 @@
     private static native void nativeSetGlobalShadowSettings(@Size(4) float[] ambientColor,
             @Size(4) float[] spotColor, float lightPosY, float lightPosZ, float lightRadius);
 
+    private static native void nativeSetFrameRate(
+            long transactionObj, long nativeObject, float frameRate);
+
     private final CloseGuard mCloseGuard = CloseGuard.get();
     private String mName;
     /**
@@ -2787,6 +2790,33 @@
         }
 
         /**
+         * Sets the intended frame rate for the surface {@link SurfaceControl}.
+         *
+         * On devices that are capable of running the display at different refresh rates, the system
+         * may choose a display refresh rate to better match this surface's frame rate. Usage of
+         * this API won't directly affect the application's frame production pipeline. However,
+         * because the system may change the display refresh rate, calls to this function may result
+         * in changes to Choreographer callback timings, and changes to the time interval at which
+         * the system releases buffers back to the application.
+         *
+         * @param sc The SurfaceControl to specify the frame rate of.
+         * @param frameRate The intended frame rate for this surface. 0 is a special value that
+         *                  indicates the app will accept the system's choice for the display frame
+         *                  rate, which is the default behavior if this function isn't called. The
+         *                  frameRate param does *not* need to be a 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.
+         * @return This transaction object.
+         */
+        @NonNull
+        public Transaction setFrameRate(
+                @NonNull SurfaceControl sc, @FloatRange(from = 0.0) float frameRate) {
+            checkPreconditions(sc);
+            nativeSetFrameRate(mNativeObject, sc.mNativeObject, frameRate);
+            return this;
+        }
+
+        /**
          * Merge the other transaction into this transaction, clearing the
          * other transaction as if it had been applied.
          *
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 01b5920..b01083b 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -413,6 +413,12 @@
     return anw->perform(surface, NATIVE_WINDOW_SET_AUTO_REFRESH, int(enabled));
 }
 
+static jint nativeSetFrameRate(JNIEnv* env, jclass clazz, jlong nativeObject, jfloat frameRate) {
+    Surface* surface = reinterpret_cast<Surface*>(nativeObject);
+    ANativeWindow* anw = static_cast<ANativeWindow*>(surface);
+    return anw->perform(surface, NATIVE_WINDOW_SET_FRAME_RATE, float(frameRate));
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gSurfaceMethods[] = {
@@ -447,6 +453,7 @@
             (void*)nativeAttachAndQueueBufferWithColorSpace},
     {"nativeSetSharedBufferModeEnabled", "(JZ)I", (void*)nativeSetSharedBufferModeEnabled},
     {"nativeSetAutoRefreshEnabled", "(JZ)I", (void*)nativeSetAutoRefreshEnabled},
+    {"nativeSetFrameRate", "(JF)I", (void*)nativeSetFrameRate},
 };
 
 int register_android_view_Surface(JNIEnv* env)
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index e0f9571..2b9d454 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -584,6 +584,14 @@
     transaction->setShadowRadius(ctrl, shadowRadius);
 }
 
+static void nativeSetFrameRate(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
+                               jfloat frameRate) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+    const auto ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+    transaction->setFrameRate(ctrl, frameRate);
+}
+
 static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) {
     const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
     jlongArray array = env->NewLongArray(displayIds.size());
@@ -1383,6 +1391,8 @@
             (void*)nativeSetLayerStack },
     {"nativeSetShadowRadius", "(JJF)V",
             (void*)nativeSetShadowRadius },
+    {"nativeSetFrameRate", "(JJF)V",
+            (void*)nativeSetFrameRate },
     {"nativeGetPhysicalDisplayIds", "()[J",
             (void*)nativeGetPhysicalDisplayIds },
     {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 203adfc..97b861b 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -240,6 +240,7 @@
     ASurfaceTransaction_setColor; # introduced=29
     ASurfaceTransaction_setDamageRegion; # introduced=29
     ASurfaceTransaction_setDesiredPresentTime; # introduced=29
+    ASurfaceTransaction_setFrameRate; # introduced=30
     ASurfaceTransaction_setGeometry; # introduced=29
     ASurfaceTransaction_setHdrMetadata_cta861_3; # introduced=29
     ASurfaceTransaction_setHdrMetadata_smpte2086; # introduced=29
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index b34b31a..392c9f6 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -545,3 +545,18 @@
 
     transaction->setBackgroundColor(surfaceControl, color, alpha, static_cast<ui::Dataspace>(dataspace));
 }
+
+void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* aSurfaceTransaction,
+                                      ASurfaceControl* aSurfaceControl, float frameRate) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+    if (frameRate < 0) {
+        ALOGE("Failed to set frame ate - invalid frame rate");
+        return;
+    }
+
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+    transaction->setFrameRate(surfaceControl, frameRate);
+}