Loading core/java/android/view/View.java +67 −19 Original line number Diff line number Diff line Loading @@ -5537,10 +5537,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f; private static final long INFREQUENT_UPDATE_INTERVAL_MILLIS = 100; private static final int INFREQUENT_UPDATE_COUNTS = 2; // 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; private int mInfrequentUpdateCount = 0; private long mLastUpdateTimeMillis = 0; private long mMinusOneFrameIntervalMillis = 0; private long mMinusTwoFrameIntervalMillis = 0; private int mLastFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; @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) Loading Loading @@ -20253,7 +20263,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } // For VRR to vote the preferred frame rate if (sToolkitSetFrameRateReadOnlyFlagValue) { updateInfrequentCount(); votePreferredFrameRate(); } // Reset content capture caches mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK; Loading Loading @@ -20358,7 +20371,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, protected void damageInParent() { if (mParent != null && mAttachInfo != null) { // For VRR to vote the preferred frame rate if (sToolkitSetFrameRateReadOnlyFlagValue) { updateInfrequentCount(); votePreferredFrameRate(); } mParent.onDescendantInvalidated(this, this); } } Loading Loading @@ -33131,11 +33147,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } private int calculateFrameRateCategory(float sizePercentage) { if (mMinusTwoFrameIntervalMillis + mMinusOneFrameIntervalMillis < INFREQUENT_UPDATE_INTERVAL_MILLIS) { if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) { return FRAME_RATE_CATEGORY_LOW; return FRAME_RATE_CATEGORY_NORMAL; } else { return FRAME_RATE_CATEGORY_HIGH; } } if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) { return FRAME_RATE_CATEGORY_NORMAL; } return mLastFrameRateCategory; } private void votePreferredFrameRate() { Loading @@ -33144,7 +33169,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, float sizePercentage = getSizePercentage(); int frameRateCateogry = calculateFrameRateCategory(sizePercentage); if (viewRootImpl != null && sizePercentage > 0) { if (sToolkitSetFrameRateReadOnlyFlagValue) { if (mPreferredFrameRate < 0) { if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) { frameRateCateogry = FRAME_RATE_CATEGORY_NO_PREFERENCE; Loading @@ -33159,7 +33183,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, viewRootImpl.votePreferredFrameRate(mPreferredFrameRate); } viewRootImpl.votePreferredFrameRateCategory(frameRateCateogry); } mLastFrameRateCategory = frameRateCateogry; if (sToolkitMetricsForFrameRateDecisionFlagValue) { viewRootImpl.recordViewPercentage(sizePercentage); } Loading Loading @@ -33238,4 +33263,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } return 0; } /** * This function is mainly used for migrating infrequent layer lagic * from SurfaceFlinger to Toolkit. * The infrequent layter logic includes: * - NORMAL for infrequent update: FT2-FT1 > 100 && FT3-FT2 > 100. * - HIGH/NORMAL based on size for frequent update: (FT3-FT2) + (FT2 - FT1) < 100. * - otherwise, use the previous category value. */ private void updateInfrequentCount() { long currentTimeMillis = AnimationUtils.currentAnimationTimeMillis(); long timeIntervalMillis = currentTimeMillis - mLastUpdateTimeMillis; mMinusTwoFrameIntervalMillis = mMinusOneFrameIntervalMillis; mMinusOneFrameIntervalMillis = timeIntervalMillis; mLastUpdateTimeMillis = currentTimeMillis; if (timeIntervalMillis >= INFREQUENT_UPDATE_INTERVAL_MILLIS) { mInfrequentUpdateCount = mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS ? mInfrequentUpdateCount : mInfrequentUpdateCount + 1; } else { mInfrequentUpdateCount = 0; } } } core/java/android/view/ViewRootImpl.java +8 −1 Original line number Diff line number Diff line Loading @@ -1025,6 +1025,13 @@ public final class ViewRootImpl implements ViewParent, // time for revaluating the idle status before lowering the frame rate. private static final int FRAME_RATE_IDLENESS_REEVALUATE_TIME = 500; /* * the variables below are used to determine whther a dVRR feature should be enabled */ // Used to determine whether to suppress boost on typing private boolean mShouldSuppressBoostOnTyping = false; /** * A temporary object used so relayoutWindow can return the latest SyncSeqId * system. The SyncSeqId system was designed to work without synchronous relayout Loading Loading @@ -12285,7 +12292,7 @@ public final class ViewRootImpl implements ViewParent, boolean desiredAction = motionEventAction == MotionEvent.ACTION_DOWN || motionEventAction == MotionEvent.ACTION_MOVE || motionEventAction == MotionEvent.ACTION_UP; boolean undesiredType = windowType == TYPE_INPUT_METHOD; boolean undesiredType = windowType == TYPE_INPUT_METHOD && mShouldSuppressBoostOnTyping; // use toolkitSetFrameRate flag to gate the change return desiredAction && !undesiredType && sToolkitSetFrameRateReadOnlyFlagValue && getFrameRateBoostOnTouchEnabled(); core/java/android/view/flags/refresh_rate_flags.aconfig +24 −0 Original line number Diff line number Diff line Loading @@ -51,3 +51,27 @@ flag { bug: "301343249" is_fixed_read_only: true } flag { name: "toolkit_frame_rate_default_normal_read_only" namespace: "toolkit" description: "Feature flag for setting frame rate category as NORMAL for default" bug: "239979904" is_fixed_read_only: true } flag { name: "toolkit_frame_rate_by_size_read_only" namespace: "toolkit" description: "Feature flag for setting frame rate category based on size" bug: "239979904" is_fixed_read_only: true } flag { name: "toolkit_frame_rate_velocity_mapping_read_only" namespace: "toolkit" description: "Feature flag for setting frame rate based on velocity" bug: "239979904" is_fixed_read_only: true } No newline at end of file core/tests/coretests/src/android/view/ViewRootImplTest.java +155 −6 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.view; import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR; import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY; import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY; 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; Loading Loading @@ -475,8 +476,9 @@ public class ViewRootImplTest { * Also, mIsFrameRateBoosting should be true when the visibility becomes visible */ @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) public void votePreferredFrameRate_voteFrameRateCategory_visibility() { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_visibility_bySize() { View view = new View(sContext); attachViewToWindow(view); ViewRootImpl viewRootImpl = view.getViewRootImpl(); Loading Loading @@ -507,8 +509,9 @@ public class ViewRootImplTest { * <7%: FRAME_RATE_CATEGORY_LOW */ @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) public void votePreferredFrameRate_voteFrameRateCategory_smallSize() { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_smallSize_bySize() { View view = new View(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 Loading @@ -534,8 +537,9 @@ public class ViewRootImplTest { * >=7% : FRAME_RATE_CATEGORY_NORMAL */ @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) public void votePreferredFrameRate_voteFrameRateCategory_normalSize() { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_normalSize_bySize() { View view = new View(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 Loading @@ -558,6 +562,96 @@ public class ViewRootImplTest { }); } /** * Test the value of the frame rate cateogry based on the visibility of a view * Invsible: FRAME_RATE_CATEGORY_NO_PREFERENCE * Visible: FRAME_RATE_CATEGORY_HIGH * Also, mIsFrameRateBoosting should be true when the visibility becomes visible */ @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) public void votePreferredFrameRate_voteFrameRateCategory_visibility_defaultHigh() { View view = new View(sContext); attachViewToWindow(view); ViewRootImpl viewRootImpl = view.getViewRootImpl(); sInstrumentation.runOnMainSync(() -> { view.setVisibility(View.INVISIBLE); view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NO_PREFERENCE); }); sInstrumentation.waitForIdleSync(); sInstrumentation.runOnMainSync(() -> { view.setVisibility(View.VISIBLE); view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); }); sInstrumentation.waitForIdleSync(); sInstrumentation.runOnMainSync(() -> { assertEquals(viewRootImpl.getIsFrameRateBoosting(), true); }); } /** * Test the value of the frame rate cateogry based on the size of a view. * The current threshold value is 7% of the screen size * <7%: FRAME_RATE_CATEGORY_NORMAL */ @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) public void votePreferredFrameRate_voteFrameRateCategory_smallSize_defaultHigh() { View view = new View(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 wmlp.width = 1; wmlp.height = 1; sInstrumentation.runOnMainSync(() -> { WindowManager wm = sContext.getSystemService(WindowManager.class); wm.addView(view, wmlp); }); sInstrumentation.waitForIdleSync(); ViewRootImpl viewRootImpl = view.getViewRootImpl(); sInstrumentation.runOnMainSync(() -> { view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL); }); } /** * Test the value of the frame rate cateogry based on the size of a view. * The current threshold value is 7% of the screen size * >=7% : FRAME_RATE_CATEGORY_HIGH */ @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) public void votePreferredFrameRate_voteFrameRateCategory_normalSize_defaultHigh() { View view = new View(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(() -> { view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); }); } /** * Test how values of the frame rate cateogry are aggregated. * It should take the max value among all of the voted categories per frame. Loading Loading @@ -701,6 +795,61 @@ public class ViewRootImplTest { }); } /** * Test the logic of infrequent layer: * - NORMAL for infrequent update: FT2-FT1 > 100 && FT3-FT2 > 100. * - HIGH/NORMAL based on size for frequent update: (FT3-FT2) + (FT2 - FT1) < 100. * - otherwise, use the previous category value. */ @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) public void votePreferredFrameRate_infrequentLayer_defaultHigh() throws InterruptedException { final long delay = 200L; View view = new View(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(); // Frequent update sInstrumentation.runOnMainSync(() -> { assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NO_PREFERENCE); view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); }); // In transistion from frequent update to infrequent update Thread.sleep(delay); sInstrumentation.runOnMainSync(() -> { view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); }); // Infrequent update Thread.sleep(delay); sInstrumentation.runOnMainSync(() -> { view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL); }); } @Test public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() { mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); Loading Loading
core/java/android/view/View.java +67 −19 Original line number Diff line number Diff line Loading @@ -5537,10 +5537,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f; private static final long INFREQUENT_UPDATE_INTERVAL_MILLIS = 100; private static final int INFREQUENT_UPDATE_COUNTS = 2; // 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; private int mInfrequentUpdateCount = 0; private long mLastUpdateTimeMillis = 0; private long mMinusOneFrameIntervalMillis = 0; private long mMinusTwoFrameIntervalMillis = 0; private int mLastFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; @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) Loading Loading @@ -20253,7 +20263,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } // For VRR to vote the preferred frame rate if (sToolkitSetFrameRateReadOnlyFlagValue) { updateInfrequentCount(); votePreferredFrameRate(); } // Reset content capture caches mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK; Loading Loading @@ -20358,7 +20371,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, protected void damageInParent() { if (mParent != null && mAttachInfo != null) { // For VRR to vote the preferred frame rate if (sToolkitSetFrameRateReadOnlyFlagValue) { updateInfrequentCount(); votePreferredFrameRate(); } mParent.onDescendantInvalidated(this, this); } } Loading Loading @@ -33131,11 +33147,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } private int calculateFrameRateCategory(float sizePercentage) { if (mMinusTwoFrameIntervalMillis + mMinusOneFrameIntervalMillis < INFREQUENT_UPDATE_INTERVAL_MILLIS) { if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) { return FRAME_RATE_CATEGORY_LOW; return FRAME_RATE_CATEGORY_NORMAL; } else { return FRAME_RATE_CATEGORY_HIGH; } } if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) { return FRAME_RATE_CATEGORY_NORMAL; } return mLastFrameRateCategory; } private void votePreferredFrameRate() { Loading @@ -33144,7 +33169,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, float sizePercentage = getSizePercentage(); int frameRateCateogry = calculateFrameRateCategory(sizePercentage); if (viewRootImpl != null && sizePercentage > 0) { if (sToolkitSetFrameRateReadOnlyFlagValue) { if (mPreferredFrameRate < 0) { if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) { frameRateCateogry = FRAME_RATE_CATEGORY_NO_PREFERENCE; Loading @@ -33159,7 +33183,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, viewRootImpl.votePreferredFrameRate(mPreferredFrameRate); } viewRootImpl.votePreferredFrameRateCategory(frameRateCateogry); } mLastFrameRateCategory = frameRateCateogry; if (sToolkitMetricsForFrameRateDecisionFlagValue) { viewRootImpl.recordViewPercentage(sizePercentage); } Loading Loading @@ -33238,4 +33263,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } return 0; } /** * This function is mainly used for migrating infrequent layer lagic * from SurfaceFlinger to Toolkit. * The infrequent layter logic includes: * - NORMAL for infrequent update: FT2-FT1 > 100 && FT3-FT2 > 100. * - HIGH/NORMAL based on size for frequent update: (FT3-FT2) + (FT2 - FT1) < 100. * - otherwise, use the previous category value. */ private void updateInfrequentCount() { long currentTimeMillis = AnimationUtils.currentAnimationTimeMillis(); long timeIntervalMillis = currentTimeMillis - mLastUpdateTimeMillis; mMinusTwoFrameIntervalMillis = mMinusOneFrameIntervalMillis; mMinusOneFrameIntervalMillis = timeIntervalMillis; mLastUpdateTimeMillis = currentTimeMillis; if (timeIntervalMillis >= INFREQUENT_UPDATE_INTERVAL_MILLIS) { mInfrequentUpdateCount = mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS ? mInfrequentUpdateCount : mInfrequentUpdateCount + 1; } else { mInfrequentUpdateCount = 0; } } }
core/java/android/view/ViewRootImpl.java +8 −1 Original line number Diff line number Diff line Loading @@ -1025,6 +1025,13 @@ public final class ViewRootImpl implements ViewParent, // time for revaluating the idle status before lowering the frame rate. private static final int FRAME_RATE_IDLENESS_REEVALUATE_TIME = 500; /* * the variables below are used to determine whther a dVRR feature should be enabled */ // Used to determine whether to suppress boost on typing private boolean mShouldSuppressBoostOnTyping = false; /** * A temporary object used so relayoutWindow can return the latest SyncSeqId * system. The SyncSeqId system was designed to work without synchronous relayout Loading Loading @@ -12285,7 +12292,7 @@ public final class ViewRootImpl implements ViewParent, boolean desiredAction = motionEventAction == MotionEvent.ACTION_DOWN || motionEventAction == MotionEvent.ACTION_MOVE || motionEventAction == MotionEvent.ACTION_UP; boolean undesiredType = windowType == TYPE_INPUT_METHOD; boolean undesiredType = windowType == TYPE_INPUT_METHOD && mShouldSuppressBoostOnTyping; // use toolkitSetFrameRate flag to gate the change return desiredAction && !undesiredType && sToolkitSetFrameRateReadOnlyFlagValue && getFrameRateBoostOnTouchEnabled();
core/java/android/view/flags/refresh_rate_flags.aconfig +24 −0 Original line number Diff line number Diff line Loading @@ -51,3 +51,27 @@ flag { bug: "301343249" is_fixed_read_only: true } flag { name: "toolkit_frame_rate_default_normal_read_only" namespace: "toolkit" description: "Feature flag for setting frame rate category as NORMAL for default" bug: "239979904" is_fixed_read_only: true } flag { name: "toolkit_frame_rate_by_size_read_only" namespace: "toolkit" description: "Feature flag for setting frame rate category based on size" bug: "239979904" is_fixed_read_only: true } flag { name: "toolkit_frame_rate_velocity_mapping_read_only" namespace: "toolkit" description: "Feature flag for setting frame rate based on velocity" bug: "239979904" is_fixed_read_only: true } No newline at end of file
core/tests/coretests/src/android/view/ViewRootImplTest.java +155 −6 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.view; import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR; import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY; import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY; 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; Loading Loading @@ -475,8 +476,9 @@ public class ViewRootImplTest { * Also, mIsFrameRateBoosting should be true when the visibility becomes visible */ @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) public void votePreferredFrameRate_voteFrameRateCategory_visibility() { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_visibility_bySize() { View view = new View(sContext); attachViewToWindow(view); ViewRootImpl viewRootImpl = view.getViewRootImpl(); Loading Loading @@ -507,8 +509,9 @@ public class ViewRootImplTest { * <7%: FRAME_RATE_CATEGORY_LOW */ @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) public void votePreferredFrameRate_voteFrameRateCategory_smallSize() { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_smallSize_bySize() { View view = new View(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 Loading @@ -534,8 +537,9 @@ public class ViewRootImplTest { * >=7% : FRAME_RATE_CATEGORY_NORMAL */ @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) public void votePreferredFrameRate_voteFrameRateCategory_normalSize() { @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, FLAG_TOOLKIT_FRAME_RATE_BY_SIZE_READ_ONLY}) public void votePreferredFrameRate_voteFrameRateCategory_normalSize_bySize() { View view = new View(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 Loading @@ -558,6 +562,96 @@ public class ViewRootImplTest { }); } /** * Test the value of the frame rate cateogry based on the visibility of a view * Invsible: FRAME_RATE_CATEGORY_NO_PREFERENCE * Visible: FRAME_RATE_CATEGORY_HIGH * Also, mIsFrameRateBoosting should be true when the visibility becomes visible */ @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) public void votePreferredFrameRate_voteFrameRateCategory_visibility_defaultHigh() { View view = new View(sContext); attachViewToWindow(view); ViewRootImpl viewRootImpl = view.getViewRootImpl(); sInstrumentation.runOnMainSync(() -> { view.setVisibility(View.INVISIBLE); view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NO_PREFERENCE); }); sInstrumentation.waitForIdleSync(); sInstrumentation.runOnMainSync(() -> { view.setVisibility(View.VISIBLE); view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); }); sInstrumentation.waitForIdleSync(); sInstrumentation.runOnMainSync(() -> { assertEquals(viewRootImpl.getIsFrameRateBoosting(), true); }); } /** * Test the value of the frame rate cateogry based on the size of a view. * The current threshold value is 7% of the screen size * <7%: FRAME_RATE_CATEGORY_NORMAL */ @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) public void votePreferredFrameRate_voteFrameRateCategory_smallSize_defaultHigh() { View view = new View(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 wmlp.width = 1; wmlp.height = 1; sInstrumentation.runOnMainSync(() -> { WindowManager wm = sContext.getSystemService(WindowManager.class); wm.addView(view, wmlp); }); sInstrumentation.waitForIdleSync(); ViewRootImpl viewRootImpl = view.getViewRootImpl(); sInstrumentation.runOnMainSync(() -> { view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL); }); } /** * Test the value of the frame rate cateogry based on the size of a view. * The current threshold value is 7% of the screen size * >=7% : FRAME_RATE_CATEGORY_HIGH */ @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) public void votePreferredFrameRate_voteFrameRateCategory_normalSize_defaultHigh() { View view = new View(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(() -> { view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); }); } /** * Test how values of the frame rate cateogry are aggregated. * It should take the max value among all of the voted categories per frame. Loading Loading @@ -701,6 +795,61 @@ public class ViewRootImplTest { }); } /** * Test the logic of infrequent layer: * - NORMAL for infrequent update: FT2-FT1 > 100 && FT3-FT2 > 100. * - HIGH/NORMAL based on size for frequent update: (FT3-FT2) + (FT2 - FT1) < 100. * - otherwise, use the previous category value. */ @Test @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) public void votePreferredFrameRate_infrequentLayer_defaultHigh() throws InterruptedException { final long delay = 200L; View view = new View(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(); // Frequent update sInstrumentation.runOnMainSync(() -> { assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NO_PREFERENCE); view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); }); // In transistion from frequent update to infrequent update Thread.sleep(delay); sInstrumentation.runOnMainSync(() -> { view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH); }); // Infrequent update Thread.sleep(delay); sInstrumentation.runOnMainSync(() -> { view.invalidate(); assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL); }); } @Test public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() { mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); Loading