summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt7
-rw-r--r--core/java/android/view/View.java79
-rw-r--r--core/java/android/view/ViewRootImpl.java40
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java52
4 files changed, 172 insertions, 6 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 7e14ae1612fa..1c9adcb595d1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -51977,6 +51977,7 @@ package android.view {
method public android.view.PointerIcon getPointerIcon();
method @NonNull public final java.util.List<android.graphics.Rect> getPreferKeepClearRects();
method @Nullable public String[] getReceiveContentMimeTypes();
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public float getRequestedFrameRate();
method public android.content.res.Resources getResources();
method public final boolean getRevealOnFocusHint();
method public final int getRight();
@@ -52353,6 +52354,7 @@ package android.view {
method public final void setPreferKeepClearRects(@NonNull java.util.List<android.graphics.Rect>);
method public void setPressed(boolean);
method public void setRenderEffect(@Nullable android.graphics.RenderEffect);
+ method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setRequestedFrameRate(float);
method public final void setRevealOnFocusHint(boolean);
method public final void setRight(int);
method public void setRotation(float);
@@ -52535,6 +52537,11 @@ package android.view {
field protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
field protected static final int[] PRESSED_STATE_SET;
field protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET;
+ field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = 0.0f;
+ field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_HIGH = -120.0f;
+ field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_LOW = -30.0f;
+ field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_NORMAL = -60.0f;
+ field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE = -1.0f;
field public static final android.util.Property<android.view.View,java.lang.Float> ROTATION;
field public static final android.util.Property<android.view.View,java.lang.Float> ROTATION_X;
field public static final android.util.Property<android.view.View,java.lang.Float> ROTATION_Y;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a478b3c40df5..d591f896d99a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -19,8 +19,10 @@ package android.view;
import static android.content.res.Resources.ID_NULL;
import static android.os.Trace.TRACE_TAG_APP;
import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
+import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
+import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
@@ -28,6 +30,7 @@ import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ER
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
+import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
import static android.view.flags.Flags.viewVelocityApi;
@@ -5521,6 +5524,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f;
+ // The preferred frame rate of the view that is mainly used for
+ // touch boosting, view velocity handling, and TextureView.
+ private float mPreferredFrameRate = REQUESTED_FRAME_RATE_CATEGORY_DEFAULT;
+
+ @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = 0;
+ @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public static final float REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE = -1;
+ @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public static final float REQUESTED_FRAME_RATE_CATEGORY_LOW = -30;
+ @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public static final float REQUESTED_FRAME_RATE_CATEGORY_NORMAL = -60;
+ @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public static final float REQUESTED_FRAME_RATE_CATEGORY_HIGH = -120;
+
/**
* Simple constructor to use when creating a view from code.
*
@@ -33015,9 +33033,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return (float) viewSize / screenSize;
}
- private int calculateFrameRateCategory() {
- float sizePercentage = getSizePercentage();
-
+ private int calculateFrameRateCategory(float sizePercentage) {
if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) {
return FRAME_RATE_CATEGORY_LOW;
} else {
@@ -33028,9 +33044,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private void votePreferredFrameRate() {
// use toolkitSetFrameRate flag to gate the change
ViewRootImpl viewRootImpl = getViewRootImpl();
+ float sizePercentage = getSizePercentage();
+ int frameRateCateogry = calculateFrameRateCategory(sizePercentage);
if (sToolkitSetFrameRateReadOnlyFlagValue && viewRootImpl != null
- && getSizePercentage() > 0) {
- viewRootImpl.votePreferredFrameRateCategory(calculateFrameRateCategory());
+ && sizePercentage > 0) {
+ if (mPreferredFrameRate < 0) {
+ if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) {
+ frameRateCateogry = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_LOW) {
+ frameRateCateogry = FRAME_RATE_CATEGORY_LOW;
+ } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NORMAL) {
+ frameRateCateogry = FRAME_RATE_CATEGORY_NORMAL;
+ } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_HIGH) {
+ frameRateCateogry = FRAME_RATE_CATEGORY_HIGH;
+ }
+ } else {
+ viewRootImpl.votePreferredFrameRate(mPreferredFrameRate);
+ }
+ viewRootImpl.votePreferredFrameRateCategory(frameRateCateogry);
}
}
@@ -33064,4 +33095,42 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
return 0;
}
+
+ /**
+ * You can set the preferred frame rate for a View using a positive number
+ * or by specifying the preferred frame rate category using constants, including
+ * REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE, REQUESTED_FRAME_RATE_CATEGORY_LOW,
+ * REQUESTED_FRAME_RATE_CATEGORY_NORMAL, REQUESTED_FRAME_RATE_CATEGORY_HIGH.
+ * Keep in mind that the preferred frame rate affects the frame rate for the next frame,
+ * so use this method carefully. It's important to note that the preference is valid as
+ * long as the View is invalidated.
+ *
+ * @param frameRate the preferred frame rate of the view.
+ */
+ @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void setRequestedFrameRate(float frameRate) {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ mPreferredFrameRate = frameRate;
+ }
+ }
+
+ /**
+ * Get the current preferred frame rate of the View.
+ * The value could be negative when preferred frame rate category is set
+ * instead of perferred frame rate.
+ * The frame rate category includes
+ * REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE, REQUESTED_FRAME_RATE_CATEGORY_LOW,
+ * REQUESTED_FRAME_RATE_CATEGORY_NORMAL, and REQUESTED_FRAME_RATE_CATEGORY_HIGH.
+ * Note that the frame rate value is valid as long as the View is invalidated.
+ *
+ * @return REQUESTED_FRAME_RATE_CATEGORY_DEFAULT by default,
+ * or value passed to {@link #setRequestedFrameRate(float)}.
+ */
+ @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public float getRequestedFrameRate() {
+ if (sToolkitSetFrameRateReadOnlyFlagValue) {
+ return mPreferredFrameRate;
+ }
+ return 0;
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d97dfb0f9c94..ac2c5509cac4 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -982,6 +982,9 @@ public final class ViewRootImpl implements ViewParent,
// The preferred frame rate of the view that is mainly used for
// touch boosting, view velocity handling, and TextureView.
private float mPreferredFrameRate = 0;
+ // The last preferred frame rate of the view that is mainly used to
+ // track the difference between the current preferred frame rate and the previous value.
+ private float mLastPreferredFrameRate = 0;
// Used to check if there were any view invalidations in
// the previous time frame (FRAME_RATE_IDLENESS_REEVALUATE_TIME).
private boolean mHasInvalidation = false;
@@ -3991,10 +3994,14 @@ public final class ViewRootImpl implements ViewParent,
}
// For the variable refresh rate project.
+ // We set the preferred frame rate and frame rate category at the end of performTraversals
+ // when the values are applicable.
setPreferredFrameRate(mPreferredFrameRate);
setPreferredFrameRateCategory(mPreferredFrameRateCategory);
mLastPreferredFrameRateCategory = mPreferredFrameRateCategory;
mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ mLastPreferredFrameRate = mPreferredFrameRate;
+ mPreferredFrameRate = 0;
}
private void createSyncIfNeeded() {
@@ -7370,7 +7377,9 @@ public final class ViewRootImpl implements ViewParent,
*/
if (mIsFrameRateBoosting && (action == MotionEvent.ACTION_UP
|| action == MotionEvent.ACTION_CANCEL)) {
- sendDelayedEmptyMessage(MSG_TOUCH_BOOST_TIMEOUT, FRAME_RATE_TOUCH_BOOST_TIME);
+ mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
+ mHandler.sendEmptyMessageDelayed(MSG_TOUCH_BOOST_TIMEOUT,
+ FRAME_RATE_TOUCH_BOOST_TIME);
}
return handled ? FINISH_HANDLED : FORWARD;
}
@@ -12062,6 +12071,35 @@ public final class ViewRootImpl implements ViewParent,
}
/**
+ * Allow Views to vote for the preferred frame rate
+ * When determining the preferred frame rate value,
+ * we follow this logic: If no preferred frame rate has been set yet,
+ * we assign the value of frameRate as the preferred frame rate.
+ * If either the current or the new preferred frame rate exceeds 60 Hz,
+ * we select the higher value between them.
+ * However, if both values are 60 Hz or lower, we set the preferred frame rate
+ * to 60 Hz to maintain optimal performance.
+ *
+ * @param frameRate the preferred frame rate of a View
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
+ public void votePreferredFrameRate(float frameRate) {
+ if (frameRate <= 0) {
+ return;
+ }
+
+ if (mPreferredFrameRate == 0) {
+ mPreferredFrameRate = frameRate;
+ } else if (frameRate > 60 || mPreferredFrameRate > 60) {
+ mPreferredFrameRate = Math.max(mPreferredFrameRate, frameRate);
+ } else if (mPreferredFrameRate != frameRate) {
+ mPreferredFrameRate = 60;
+ }
+
+ mHasInvalidation = true;
+ }
+
+ /**
* Get the value of mPreferredFrameRateCategory
*/
@VisibleForTesting
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index dfe6cf81813d..e0e3a3542cb0 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -577,6 +577,58 @@ public class ViewRootImplTest {
});
}
+ /**
+ * Test the accurate aggregation of frame rate values as follows:
+ * 1. When values exceed 60Hz, select the maximum value.
+ * 2. If frame rates are less than 60Hz and multiple frame rates are voted,
+ * prioritize 60Hz..
+ */
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void votePreferredFrameRate_voteFrameRate_aggregate() {
+ View view = new View(sContext);
+ attachViewToWindow(view);
+ sInstrumentation.runOnMainSync(() -> {
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
+ viewRootImpl.votePreferredFrameRate(24);
+ assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
+ viewRootImpl.votePreferredFrameRate(30);
+ assertEquals(viewRootImpl.getPreferredFrameRate(), 60, 0.1);
+ viewRootImpl.votePreferredFrameRate(120);
+ assertEquals(viewRootImpl.getPreferredFrameRate(), 120, 0.1);
+ });
+ }
+
+ /**
+ * Override the frame rate category value with setRequestedFrameRate method.
+ * This function can replace the existing frameRateCategory value and
+ * submit your preferred choice to the ViewRootImpl.
+ */
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void votePreferredFrameRate_voteFrameRate_category() {
+ View view = new View(sContext);
+ attachViewToWindow(view);
+ sInstrumentation.waitForIdleSync();
+
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+ sInstrumentation.runOnMainSync(() -> {
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+ FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NORMAL);
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ });
+ }
+
+
@Test
public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);