Merge "Fix frame rate idleness and TextureView video play logic" into main
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 896b3f4..124aece 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -883,6 +885,17 @@
mListener = listener;
}
+ /**
+ * @hide
+ */
+ @Override
+ protected int calculateFrameRateCategory(float sizePercentage) {
+ if (mMinusTwoFrameIntervalMillis > 15 && mMinusOneFrameIntervalMillis > 15) {
+ return FRAME_RATE_CATEGORY_NORMAL;
+ }
+ return super.calculateFrameRateCategory(sizePercentage);
+ }
+
@UnsupportedAppUsage
private final SurfaceTexture.OnFrameAvailableListener mUpdateListener =
surfaceTexture -> {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4df95bf..22d34e6 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5649,9 +5649,15 @@
private int mInfrequentUpdateCount = 0;
private long mLastUpdateTimeMillis = 0;
- private long mMinusOneFrameIntervalMillis = 0;
- private long mMinusTwoFrameIntervalMillis = 0;
- private int mLastFrameRateCategory = FRAME_RATE_CATEGORY_HIGH;
+ /**
+ * @hide
+ */
+ protected long mMinusOneFrameIntervalMillis = 0;
+ /**
+ * @hide
+ */
+ protected long mMinusTwoFrameIntervalMillis = 0;
+ private int mLastFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
@FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = Float.NaN;
@@ -33612,7 +33618,12 @@
return (float) viewSize / screenSize;
}
- private int calculateFrameRateCategory(float sizePercentage) {
+ /**
+ * Used to calculate the frame rate category of a View.
+ *
+ * @hide
+ */
+ protected int calculateFrameRateCategory(float sizePercentage) {
if (mMinusTwoFrameIntervalMillis + mMinusOneFrameIntervalMillis
< INFREQUENT_UPDATE_INTERVAL_MILLIS) {
if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) {
@@ -33746,7 +33757,10 @@
mMinusTwoFrameIntervalMillis = mMinusOneFrameIntervalMillis;
mMinusOneFrameIntervalMillis = timeIntervalMillis;
- mLastUpdateTimeMillis = currentTimeMillis;
+ if (mMinusOneFrameIntervalMillis - mMinusTwoFrameIntervalMillis >= 30
+ && timeIntervalMillis < 2) {
+ return;
+ }
if (timeIntervalMillis >= INFREQUENT_UPDATE_INTERVAL_MILLIS) {
mInfrequentUpdateCount = mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS
? mInfrequentUpdateCount : mInfrequentUpdateCount + 1;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3c7c82f..b5f3b9a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1043,7 +1043,7 @@
// time for checking idle status periodically.
private static final int FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS = 500;
// time for revaluating the idle status before lowering the frame rate.
- private static final int FRAME_RATE_IDLENESS_REEVALUATE_TIME = 500;
+ private static final int FRAME_RATE_IDLENESS_REEVALUATE_TIME = 1000;
// time for evaluating the interval between current time and
// the time when frame rate was set previously.
private static final int FRAME_RATE_SETTING_REEVALUATE_TIME = 100;
@@ -6540,6 +6540,7 @@
mHasInvalidation = false;
mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
FRAME_RATE_IDLENESS_REEVALUATE_TIME);
+ mHasIdledMessage = true;
}
break;
case MSG_REFRESH_POINTER_ICON:
@@ -12368,14 +12369,6 @@
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
-
- if (mPreferredFrameRateCategory != FRAME_RATE_CATEGORY_NO_PREFERENCE && !mHasIdledMessage) {
- // Check where the display is idled periodically.
- // If so, set the frame rate category to NO_PREFERENCE
- mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
- FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS);
- mHasIdledMessage = true;
- }
}
private void setPreferredFrameRate(float preferredFrameRate) {
@@ -12389,7 +12382,8 @@
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(
Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRate "
- + preferredFrameRate);
+ + preferredFrameRate + " compatibility "
+ + mFrameRateCompatibility);
}
mFrameRateTransaction.setFrameRate(mSurfaceControl, preferredFrameRate,
mFrameRateCompatibility).applyAsyncUnsafe();
@@ -12415,7 +12409,7 @@
private boolean shouldSetFrameRate() {
// use toolkitSetFrameRate flag to gate the change
- return mSurface.isValid() && mPreferredFrameRate > 0
+ return mSurface.isValid() && mPreferredFrameRate >= 0
&& shouldEnableDvrr() && !mIsFrameRateConflicted;
}
@@ -12456,6 +12450,7 @@
mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_LOW;
}
mHasInvalidation = true;
+ checkIdleness();
}
/**
@@ -12498,6 +12493,7 @@
mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING,
FRAME_RATE_SETTING_REEVALUATE_TIME);
}
+ checkIdleness();
}
/**
@@ -12603,4 +12599,14 @@
private boolean shouldEnableDvrr() {
return sToolkitSetFrameRateReadOnlyFlagValue && mIsFrameRatePowerSavingsBalanced;
}
+
+ private void checkIdleness() {
+ if (!mHasIdledMessage) {
+ // Check where the display is idled periodically.
+ // If so, set the frame rate category to NO_PREFERENCE
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
+ FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS);
+ mHasIdledMessage = true;
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 64cbe7f..7c58de6 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -1017,6 +1017,60 @@
assertEquals(viewRootImpl.isFrameRatePowerSavingsBalanced(), true);
}
+ /**
+ * Test the TextureView heuristic:
+ * 1. Store the last 3 invalidates time - FT1, FT2, FT3.
+ * 2. If FT2-FT1 > 15ms && FT3-FT2 > 15ms -> vote for NORMAL category
+ */
+ @Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
+ public void votePreferredFrameRate_applyTextureViewHeuristic() throws InterruptedException {
+ final long delay = 30L;
+
+ TextureView view = new TextureView(sContext);
+ WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+ wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
+
+ sInstrumentation.runOnMainSync(() -> {
+ WindowManager wm = sContext.getSystemService(WindowManager.class);
+ Display display = wm.getDefaultDisplay();
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getMetrics(metrics);
+ wmlp.width = (int) (metrics.widthPixels * 0.9);
+ wmlp.height = (int) (metrics.heightPixels * 0.9);
+ wm.addView(view, wmlp);
+ });
+ sInstrumentation.waitForIdleSync();
+
+ ViewRootImpl viewRootImpl = view.getViewRootImpl();
+
+ sInstrumentation.runOnMainSync(() -> {
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+ FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+ FRAME_RATE_CATEGORY_HIGH);
+ });
+
+ // reset the frame rate category counts
+ for (int i = 0; i < 5; i++) {
+ Thread.sleep(delay);
+ sInstrumentation.runOnMainSync(() -> {
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ view.invalidate();
+ });
+ sInstrumentation.waitForIdleSync();
+ }
+
+ Thread.sleep(delay);
+ sInstrumentation.runOnMainSync(() -> {
+ view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT);
+ view.invalidate();
+ assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
+ FRAME_RATE_CATEGORY_NORMAL);
+ });
+ }
+
@Test
public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);