Add shouldBeSeamless param to Surface.setFrameRate

This CL adds a new parameter shouldBeSeamless to the existing
setFrameRate APIs. This parameter indicates whether the desired
refresh rate should be achieved only seamlessly or also switches
with visual interruptions for the user are allowed. The default
value of the new parameter is "true".

Test: atest SetFrameRateTest
Test: atest RefreshRateConfigsTest
Test: atest libsurfaceflinger_unittest
Bug: 161776961
Change-Id: Ic2446d278e4f57fe507d30a0a18ef7b85909da4b
diff --git a/core/api/current.txt b/core/api/current.txt
index 629bfe9..ba52ccae 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -52173,6 +52173,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, int, boolean);
     method public void setFrameRate(@FloatRange(from=0.0) float, int);
     method @Deprecated public void unlockCanvas(android.graphics.Canvas);
     method public void unlockCanvasAndPost(android.graphics.Canvas);
@@ -52220,6 +52221,7 @@
     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, int);
+    method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int, boolean);
     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 5b79174..a2777fe 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -97,7 +97,7 @@
     private static native int nativeSetAutoRefreshEnabled(long nativeObject, boolean enabled);
 
     private static native int nativeSetFrameRate(
-            long nativeObject, float frameRate, int compatibility);
+            long nativeObject, float frameRate, int compatibility, boolean shouldBeSeamless);
 
     public static final @android.annotation.NonNull Parcelable.Creator<Surface> CREATOR =
             new Parcelable.Creator<Surface>() {
@@ -915,13 +915,20 @@
      * compatibility value may influence the system's choice of display frame rate. See
      * the FRAME_RATE_COMPATIBILITY_* values for more info.
      *
+     * @param shouldBeSeamless Whether display refresh rate transitions should be seamless. A
+     * seamless transition is one that doesn't have any visual interruptions, such as a black
+     * screen for a second or two. True indicates that any frame rate changes caused by this
+     * request should be seamless. False indicates that non-seamless refresh rates are also
+     * acceptable.
+     *
      * @throws IllegalArgumentException If frameRate or compatibility are invalid.
      */
-    public void setFrameRate(
-            @FloatRange(from = 0.0) float frameRate, @FrameRateCompatibility int compatibility) {
+    public void setFrameRate(@FloatRange(from = 0.0) float frameRate,
+            @FrameRateCompatibility int compatibility, boolean shouldBeSeamless) {
         synchronized (mLock) {
             checkNotReleasedLocked();
-            int error = nativeSetFrameRate(mNativeObject, frameRate, compatibility);
+            int error = nativeSetFrameRate(mNativeObject, frameRate, compatibility,
+                    shouldBeSeamless);
             if (error == -EINVAL) {
                 throw new IllegalArgumentException("Invalid argument to Surface.setFrameRate()");
             } else if (error != 0) {
@@ -931,6 +938,17 @@
     }
 
     /**
+     * Sets the intended frame rate for this surface. Any switching of refresh rates is
+     * most probably going to be seamless.
+     *
+     * @see #setFrameRate(float, int, boolean)
+     */
+    public void setFrameRate(
+            @FloatRange(from = 0.0) float frameRate, @FrameRateCompatibility int compatibility) {
+        setFrameRate(frameRate, compatibility, /* shouldBeSeamless = */ true);
+    }
+
+    /**
      * 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 af31b81..d7ee6ad 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -212,8 +212,8 @@
     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, int compatibility);
+    private static native void nativeSetFrameRate(long transactionObj, long nativeObject,
+            float frameRate, int compatibility, boolean shouldBeSeamless);
     private static native long nativeGetHandle(long nativeObject);
 
     private static native long nativeAcquireFrameRateFlexibilityToken();
@@ -3256,6 +3256,19 @@
         }
 
         /**
+         * Sets the intended frame rate for this surface. Any switching of refresh rates is
+         * most probably going to be seamless.
+         *
+         * @see #setFrameRate(SurfaceControl, float, int, boolean)
+         */
+        @NonNull
+        public Transaction setFrameRate(@NonNull SurfaceControl sc,
+                @FloatRange(from = 0.0) float frameRate,
+                @Surface.FrameRateCompatibility int compatibility) {
+            return setFrameRate(sc, frameRate, compatibility, /*shouldBeSeamless*/ true);
+        }
+
+        /**
          * Sets the intended frame rate for the surface {@link SurfaceControl}.
          * <p>
          * On devices that are capable of running the display at different refresh rates, the system
@@ -3275,14 +3288,22 @@
          * @param compatibility The frame rate compatibility of this surface. The compatibility
          *                      value may influence the system's choice of display frame rate. See
          *                      the Surface.FRAME_RATE_COMPATIBILITY_* values for more info.
+         * @param shouldBeSeamless Whether display refresh rate transitions should be seamless. A
+         *                         seamless transition is one that doesn't have any visual
+         *                         interruptions, such as a black screen for a second or two. True
+         *                         indicates that any frame rate changes caused by this request
+         *                         should be seamless. False indicates that non-seamless refresh
+         *                         rates are also acceptable.
          * @return This transaction object.
          */
         @NonNull
         public Transaction setFrameRate(@NonNull SurfaceControl sc,
                 @FloatRange(from = 0.0) float frameRate,
-                @Surface.FrameRateCompatibility int compatibility) {
+                @Surface.FrameRateCompatibility int compatibility,
+                boolean shouldBeSeamless) {
             checkPreconditions(sc);
-            nativeSetFrameRate(mNativeObject, sc.mNativeObject, frameRate, compatibility);
+            nativeSetFrameRate(mNativeObject, sc.mNativeObject, frameRate, compatibility,
+                    shouldBeSeamless);
             return this;
         }
 
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 3a1ccd9..5b29f0b 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -438,14 +438,15 @@
 }
 
 static jint nativeSetFrameRate(JNIEnv* env, jclass clazz, jlong nativeObject, jfloat frameRate,
-                               jint compatibility) {
+                               jint compatibility, jboolean shouldBeSeamless) {
     Surface* surface = reinterpret_cast<Surface*>(nativeObject);
     ANativeWindow* anw = static_cast<ANativeWindow*>(surface);
     // Our compatibility is a Surface.FRAME_RATE_COMPATIBILITY_* value, and
     // NATIVE_WINDOW_SET_FRAME_RATE takes an
     // ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value. The values are identical
     // though, so no need to explicitly convert.
-    return anw->perform(surface, NATIVE_WINDOW_SET_FRAME_RATE, float(frameRate), compatibility);
+    return anw->perform(surface, NATIVE_WINDOW_SET_FRAME_RATE, double(frameRate), compatibility,
+                        int(shouldBeSeamless));
 }
 
 // ----------------------------------------------------------------------------
@@ -474,7 +475,7 @@
          (void*)nativeAttachAndQueueBufferWithColorSpace},
         {"nativeSetSharedBufferModeEnabled", "(JZ)I", (void*)nativeSetSharedBufferModeEnabled},
         {"nativeSetAutoRefreshEnabled", "(JZ)I", (void*)nativeSetAutoRefreshEnabled},
-        {"nativeSetFrameRate", "(JFI)I", (void*)nativeSetFrameRate},
+        {"nativeSetFrameRate", "(JFIZ)I", (void*)nativeSetFrameRate},
         {"nativeGetFromBlastBufferQueue", "(JJ)J", (void*)nativeGetFromBlastBufferQueue},
 };
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index f1ec85a..6ec656c 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -738,14 +738,15 @@
 }
 
 static void nativeSetFrameRate(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
-                               jfloat frameRate, jint compatibility) {
+                               jfloat frameRate, jint compatibility, jboolean shouldBeSeamless) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
 
     const auto ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
     // Our compatibility is a Surface.FRAME_RATE_COMPATIBILITY_* value, and
     // Transaction::setFrameRate() takes an ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_* value. The
     // values are identical though, so no need to convert anything.
-    transaction->setFrameRate(ctrl, frameRate, static_cast<int8_t>(compatibility));
+    transaction->setFrameRate(ctrl, frameRate, static_cast<int8_t>(compatibility),
+                              bool(shouldBeSeamless));
 }
 
 static jlong nativeAcquireFrameRateFlexibilityToken(JNIEnv* env, jclass clazz) {
@@ -1668,7 +1669,7 @@
             (void*)nativeSetBlurRegions },
     {"nativeSetShadowRadius", "(JJF)V",
             (void*)nativeSetShadowRadius },
-    {"nativeSetFrameRate", "(JJFI)V",
+    {"nativeSetFrameRate", "(JJFIZ)V",
             (void*)nativeSetFrameRate },
     {"nativeAcquireFrameRateFlexibilityToken", "()J",
             (void*)nativeAcquireFrameRateFlexibilityToken },
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 3027eac..d134b37 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -247,6 +247,7 @@
     ASurfaceTransaction_setDamageRegion; # introduced=29
     ASurfaceTransaction_setDesiredPresentTime; # introduced=29
     ASurfaceTransaction_setFrameRate; # introduced=30
+    ASurfaceTransaction_setFrameRateWithSeamlessness; # introduced=31
     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 c503721..189be80 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -560,9 +560,18 @@
 void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* aSurfaceTransaction,
                                       ASurfaceControl* aSurfaceControl, float frameRate,
                                       int8_t compatibility) {
+    ASurfaceTransaction_setFrameRateWithSeamlessness(aSurfaceTransaction, aSurfaceControl,
+                                                     frameRate, compatibility,
+                                                     /*shouldBeSeamless*/ true);
+}
+
+void ASurfaceTransaction_setFrameRateWithSeamlessness(ASurfaceTransaction* aSurfaceTransaction,
+                                                      ASurfaceControl* aSurfaceControl,
+                                                      float frameRate, int8_t compatibility,
+                                                      bool shouldBeSeamless) {
     CHECK_NOT_NULL(aSurfaceTransaction);
     CHECK_NOT_NULL(aSurfaceControl);
     Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
     sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
-    transaction->setFrameRate(surfaceControl, frameRate, compatibility);
+    transaction->setFrameRate(surfaceControl, frameRate, compatibility, shouldBeSeamless);
 }