Merge "Add new Surface#setFrameRate API" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index a8b9e33..09fef25 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -52491,6 +52491,7 @@
     method public android.graphics.Canvas lockHardwareCanvas();
     method public void readFromParcel(android.os.Parcel);
     method public void release();
+    method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public void setFrameRate(@NonNull android.view.Surface.FrameRateParams);
     method public void setFrameRate(@FloatRange(from=0.0) float, int, int);
     method public void setFrameRate(@FloatRange(from=0.0) float, int);
     method @Deprecated public void unlockCanvas(android.graphics.Canvas);
@@ -52507,6 +52508,22 @@
     field public static final int ROTATION_90 = 1; // 0x1
   }
 
+  @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public static class Surface.FrameRateParams {
+    method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public int getChangeFrameRateStrategy();
+    method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public float getDesiredMaxRate();
+    method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public float getDesiredMinRate();
+    method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public float getFixedSourceRate();
+    field @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public static final android.view.Surface.FrameRateParams IGNORE;
+  }
+
+  @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") public static final class Surface.FrameRateParams.Builder {
+    ctor public Surface.FrameRateParams.Builder();
+    method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") @NonNull public android.view.Surface.FrameRateParams build();
+    method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") @NonNull public android.view.Surface.FrameRateParams.Builder setChangeFrameRateStrategy(int);
+    method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") @NonNull public android.view.Surface.FrameRateParams.Builder setDesiredRateRange(@FloatRange(from=0.0) float, @FloatRange(from=0.0) float);
+    method @FlaggedApi("com.android.graphics.surfaceflinger.flags.arr_setframerate_api") @NonNull public android.view.Surface.FrameRateParams.Builder setFixedSourceRate(@FloatRange(from=0.0) float);
+  }
+
   public static class Surface.OutOfResourcesException extends java.lang.RuntimeException {
     ctor public Surface.OutOfResourcesException();
     ctor public Surface.OutOfResourcesException(String);
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 396be7b..03f9d98 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -18,9 +18,11 @@
 
 import static android.system.OsConstants.EINVAL;
 
+import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo;
 import android.content.res.CompatibilityInfo.Translator;
@@ -1026,6 +1028,211 @@
     }
 
     /**
+     * Parameter object for {@link #setFrameRate(FrameRateParams)}, describing the intended frame
+     * rate for the Surface that setFrameRate is called on.
+     */
+    @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API)
+    public static class FrameRateParams {
+        private FrameRateParams() {}
+
+        /**
+         * A static FrameRateParams that can be passed directly into {@link
+         * #setFrameRate(FrameRateParams)} to indicate the surface has no preference and any frame
+         * rate is acceptable.
+         */
+        @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API)
+        public static final FrameRateParams IGNORE =
+                new FrameRateParams.Builder().setDesiredRateRange(0f, Float.MAX_VALUE).build();
+
+        @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API)
+        public static final class Builder {
+            private float mDesiredMinRate;
+            private float mDesiredMaxRate;
+            private float mFixedSourceRate;
+            private int mChangeFrameRateStrategy;
+
+            /**
+             * Sets the desired frame rate range (inclusive) values for the surface, specifying that
+             * the surface prefers the device render rate to be in the range [desiredMinRate,
+             * desiredMaxRate].
+
+             * Set desiredMaxRate to FLOAT.MAX_VALUE to indicate the surface prefers any value
+             * greater than or equal to desiredMinRate.
+             *
+             * Set desiredMinRate = desiredMaxRate to indicate the surface prefers an exact frame
+             * rate. Note that this is different than specifying the fixed source frame rate with
+             * {@link FrameRateParams.Builder#setFixedSourceRate}. To reiterate, this call is used
+             * to specify the surface's frame rate preference to be within the desired range.
+             *
+             * desiredMaxRate must be greater than or equal to desiredMinRate.
+             * The values should be greater than or equal to 0.
+             *
+             * If the surface has no preference and any frame rate is acceptable, use the constant
+             * {@link FrameRateParams.IGNORE} in {@link #setFrameRate(FrameRateParams)} instead of
+             * building {@link FrameRateParams.Builder}.
+             *
+             * @see FrameRateParams#getDesiredMinRate()
+             * @see FrameRateParams#getDesiredMaxRate()
+             */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API)
+            public Builder setDesiredRateRange(@FloatRange(from = 0.0) float desiredMinRate,
+                    @FloatRange(from = 0.0) float desiredMaxRate) {
+                if (desiredMaxRate < desiredMinRate) {
+                    Log.e(TAG,
+                            "Failed to set desired frame rate range. desiredMaxRate should be "
+                                    + "greater than or equal to desiredMinRate");
+                    return this;
+                }
+                mDesiredMinRate = desiredMinRate;
+                mDesiredMaxRate = desiredMaxRate;
+                return this;
+            }
+
+            /**
+             * Sets the fixed frame rate of the surface when its content has a fixed frame rate,
+             * e.g. a video with a fixed frame rate.
+             *
+             * When the frame rate chosen for the surface is the {@code fixedSourceRate} or a
+             * multiple, the surface can render without frame pulldown, for optimal smoothness. For
+             * example, a 30 fps video ({@code fixedSourceRate=30}) renders just as well on 30 fps,
+             * 60 fps, 90 fps, 120 fps, and so on.
+             *
+             * This method to set the fixed source rate can also be used together with a desired
+             * frame rate range via {@link FrameRateParams.Builder#setDesiredRateRange}. This still
+             * means the surface's content has a fixed frame rate of the provided {@code
+             * fixedSourceRate}, as well as it preferring to be within the desired frame rate range.
+             * For example, a 30 fps video {@code fixedSourceRate=30} and desired frame rate range
+             * [60,90] means the surface ideally prefers 60 fps (which is 30 fps * 2) or 90 fps (30
+             * fps * 3).
+             *
+             * @see FrameRateParams#getFixedSourceRate()
+             */
+            @NonNull
+            @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API)
+            public Builder setFixedSourceRate(@FloatRange(from = 0.0) float fixedSourceRate) {
+                mFixedSourceRate = fixedSourceRate;
+                return this;
+            }
+
+            /**
+             * Whether display refresh rate transitions caused by this surface 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. Value is
+             * Surface.CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, or Surface.CHANGE_FRAME_RATE_ALWAYS
+             *
+             * @see FrameRateParams#getChangeFrameRateStrategy()
+             */
+            @NonNull
+            @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API)
+            public Builder setChangeFrameRateStrategy(
+                    @ChangeFrameRateStrategy int changeFrameRateStrategy) {
+                mChangeFrameRateStrategy = changeFrameRateStrategy;
+                return this;
+            }
+
+            /**
+             * Builds the FrameRateParams object.
+             */
+            @NonNull
+            @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API)
+            public FrameRateParams build() {
+                FrameRateParams frameRate = new FrameRateParams();
+                frameRate.mDesiredMinRate = this.mDesiredMinRate;
+                frameRate.mDesiredMaxRate = this.mDesiredMaxRate;
+                frameRate.mFixedSourceRate = this.mFixedSourceRate;
+                frameRate.mChangeFrameRateStrategy = this.mChangeFrameRateStrategy;
+                return frameRate;
+            }
+        }
+
+        /**
+         * Gets the minimum desired frame rate.
+         * @see FrameRateParams.Builder#setDesiredRateRange()
+         */
+        @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API)
+        public float getDesiredMinRate() {
+            return mDesiredMinRate;
+        }
+
+        /**
+         * Gets the maximum desired frame rate.
+         * @see FrameRateParams.Builder#setDesiredRateRange()
+         */
+        @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API)
+        public float getDesiredMaxRate() {
+            return mDesiredMaxRate;
+        }
+
+        /**
+         * Gets the fixed source frame rate.
+         * @see FrameRateParams.Builder#setFixedSourceRate()
+         */
+        @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API)
+        public float getFixedSourceRate() {
+            return mFixedSourceRate;
+        }
+
+        /**
+         * Gets the strategy when changing frame rate.
+         * @see FrameRateParams.Builder#setChangeFrameRateStrategy
+         */
+        @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API)
+        @ChangeFrameRateStrategy
+        public int getChangeFrameRateStrategy() {
+            return mChangeFrameRateStrategy;
+        }
+
+        float mDesiredMinRate;
+        float mDesiredMaxRate;
+        float mFixedSourceRate;
+        int mChangeFrameRateStrategy;
+    }
+
+    /**
+     * Sets the intended frame rate for this surface.
+     *
+     * <p>On devices that are capable of running the display at different frame 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.</p>
+     *
+     * <p>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.</p>
+     *
+     * @param frameRateParams The parameters describing the intended frame rate of this surface.
+     *         Refer to {@link FrameRateParams} for details.
+     * @throws IllegalArgumentException If <code>frameRateParams</code> is invalid.
+     * @see #clearFrameRate()
+     */
+    @FlaggedApi(com.android.graphics.surfaceflinger.flags.Flags.FLAG_ARR_SETFRAMERATE_API)
+    public void setFrameRate(@NonNull FrameRateParams frameRateParams) {
+        synchronized (mLock) {
+            checkNotReleasedLocked();
+            // TODO(b/362798998): Logic currently incomplete: it uses fixed source rate over the
+            // desired min/max rates. Fix when plumbing is upgraded.
+            int compatibility = frameRateParams.getFixedSourceRate() == 0
+                    ? FRAME_RATE_COMPATIBILITY_DEFAULT
+                    : FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
+            float frameRate = compatibility == FRAME_RATE_COMPATIBILITY_DEFAULT
+                    ? frameRateParams.getDesiredMinRate()
+                    : frameRateParams.getFixedSourceRate();
+            int error = nativeSetFrameRate(mNativeObject, frameRate, compatibility,
+                    frameRateParams.getChangeFrameRateStrategy());
+            if (error == -EINVAL) {
+                throw new IllegalArgumentException("Invalid argument to Surface.setFrameRate()");
+            } else if (error != 0) {
+                Log.e(TAG, "Failed to set frame rate on Surface. Native error: " + error);
+            }
+        }
+    }
+
+    /**
      * Sets the intended frame rate for this surface.
      *
      * <p>On devices that are capable of running the display at different refresh rates,