From 540012bf1e1d9d898a9312ab24487222da366eea Mon Sep 17 00:00:00 2001 From: Ady Abraham Date: Tue, 29 Jun 2021 19:25:45 -0700 Subject: [PATCH] Add preferredMinDisplayRefreshRate Add a new private window attribute for allowing apps to specify the min display refresh rate in addition to the existing preferredMaxDisplayRefreshRate. This is useful for use cases such as keyguard where the refresh rate should be limited to a single value, and using preferredDisplayModeId would not lock the display refresh rate, as frame rate override might be enabled. Test: atest RefreshRatePolicyTest FrameRateSelectionPriorityTests DisplayModeDirectorTest Bug: 183226498 Bug: 184176119 Change-Id: I343569b3cbccd73001703dca78f0f99e196a4d52 --- .../display/DisplayManagerInternal.java | 7 +- core/java/android/view/WindowManager.java | 23 ++- .../server/display/DisplayManagerService.java | 16 +- .../server/display/DisplayModeDirector.java | 69 ++++--- .../com/android/server/wm/DisplayContent.java | 11 ++ .../android/server/wm/RefreshRatePolicy.java | 15 ++ .../display/DisplayModeDirectorTest.java | 185 ++++++++++++++---- .../server/wm/RefreshRatePolicyTest.java | 37 ++++ 8 files changed, 286 insertions(+), 77 deletions(-) diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 1c0ae281d9fe7..abcc33c43c743 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -207,6 +207,8 @@ public abstract class DisplayManagerInternal { * has a preference. * @param requestedModeId The preferred mode id for the top-most visible window that has a * preference. + * @param requestedMinRefreshRate The preferred lowest refresh rate for the top-most visible + * window that has a preference. * @param requestedMaxRefreshRate The preferred highest refresh rate for the top-most visible * window that has a preference. * @param requestedMinimalPostProcessing The preferred minimal post processing setting for the @@ -216,8 +218,9 @@ public abstract class DisplayManagerInternal { * prior to call to performTraversalInTransactionFromWindowManager. */ public abstract void setDisplayProperties(int displayId, boolean hasContent, - float requestedRefreshRate, int requestedModeId, float requestedMaxRefreshRate, - boolean requestedMinimalPostProcessing, boolean inTraversal); + float requestedRefreshRate, int requestedModeId, float requestedMinRefreshRate, + float requestedMaxRefreshRate, boolean requestedMinimalPostProcessing, + boolean inTraversal); /** * Applies an offset to the contents of a display, for example to avoid burn-in. diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 2996c3d871201..55beae0f7b3d9 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -3022,6 +3022,14 @@ public interface WindowManager extends ViewManager { */ public int preferredDisplayModeId; + /** + * The min display refresh rate while the window is in focus. + * + * This value is ignored if {@link #preferredDisplayModeId} is set. + * @hide + */ + public float preferredMinDisplayRefreshRate; + /** * The max display refresh rate while the window is in focus. * @@ -3781,6 +3789,7 @@ public interface WindowManager extends ViewManager { out.writeInt(screenOrientation); out.writeFloat(preferredRefreshRate); out.writeInt(preferredDisplayModeId); + out.writeFloat(preferredMinDisplayRefreshRate); out.writeFloat(preferredMaxDisplayRefreshRate); out.writeInt(systemUiVisibility); out.writeInt(subtreeSystemUiVisibility); @@ -3852,6 +3861,7 @@ public interface WindowManager extends ViewManager { screenOrientation = in.readInt(); preferredRefreshRate = in.readFloat(); preferredDisplayModeId = in.readInt(); + preferredMinDisplayRefreshRate = in.readFloat(); preferredMaxDisplayRefreshRate = in.readFloat(); systemUiVisibility = in.readInt(); subtreeSystemUiVisibility = in.readInt(); @@ -3931,7 +3941,9 @@ public interface WindowManager extends ViewManager { /** {@hide} */ public static final int BLUR_BEHIND_RADIUS_CHANGED = 1 << 29; /** {@hide} */ - public static final int PREFERRED_MAX_DISPLAY_REFRESH_RATE = 1 << 30; + public static final int PREFERRED_MIN_DISPLAY_REFRESH_RATE = 1 << 30; + /** {@hide} */ + public static final int PREFERRED_MAX_DISPLAY_REFRESH_RATE = 1 << 31; // internal buffer to backup/restore parameters under compatibility mode. private int[] mCompatibilityParamsBackup = null; @@ -4063,6 +4075,11 @@ public interface WindowManager extends ViewManager { changes |= PREFERRED_DISPLAY_MODE_ID; } + if (preferredMinDisplayRefreshRate != o.preferredMinDisplayRefreshRate) { + preferredMinDisplayRefreshRate = o.preferredMinDisplayRefreshRate; + changes |= PREFERRED_MIN_DISPLAY_REFRESH_RATE; + } + if (preferredMaxDisplayRefreshRate != o.preferredMaxDisplayRefreshRate) { preferredMaxDisplayRefreshRate = o.preferredMaxDisplayRefreshRate; changes |= PREFERRED_MAX_DISPLAY_REFRESH_RATE; @@ -4272,6 +4289,10 @@ public interface WindowManager extends ViewManager { sb.append(" preferredDisplayMode="); sb.append(preferredDisplayModeId); } + if (preferredMinDisplayRefreshRate != 0) { + sb.append(" preferredMinDisplayRefreshRate="); + sb.append(preferredMinDisplayRefreshRate); + } if (preferredMaxDisplayRefreshRate != 0) { sb.append(" preferredMaxDisplayRefreshRate="); sb.append(preferredMaxDisplayRefreshRate); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 0decd33936723..79ea108a75d54 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1506,8 +1506,9 @@ public final class DisplayManagerService extends SystemService { } private void setDisplayPropertiesInternal(int displayId, boolean hasContent, - float requestedRefreshRate, int requestedModeId, float requestedMaxRefreshRate, - boolean preferMinimalPostProcessing, boolean inTraversal) { + float requestedRefreshRate, int requestedModeId, float requestedMinRefreshRate, + float requestedMaxRefreshRate, boolean preferMinimalPostProcessing, + boolean inTraversal) { synchronized (mSyncRoot) { final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId); if (display == null) { @@ -1538,7 +1539,7 @@ public final class DisplayManagerService extends SystemService { } } mDisplayModeDirector.getAppRequestObserver().setAppRequest( - displayId, requestedModeId, requestedMaxRefreshRate); + displayId, requestedModeId, requestedMinRefreshRate, requestedMaxRefreshRate); if (display.getDisplayInfoLocked().minimalPostProcessingSupported) { boolean mppRequest = mMinimalPostProcessingAllowed && preferMinimalPostProcessing; @@ -3208,11 +3209,12 @@ public final class DisplayManagerService extends SystemService { @Override public void setDisplayProperties(int displayId, boolean hasContent, - float requestedRefreshRate, int requestedMode, float requestedMaxRefreshRate, - boolean requestedMinimalPostProcessing, boolean inTraversal) { + float requestedRefreshRate, int requestedMode, float requestedMinRefreshRate, + float requestedMaxRefreshRate, boolean requestedMinimalPostProcessing, + boolean inTraversal) { setDisplayPropertiesInternal(displayId, hasContent, requestedRefreshRate, - requestedMode, requestedMaxRefreshRate, requestedMinimalPostProcessing, - inTraversal); + requestedMode, requestedMinRefreshRate, requestedMaxRefreshRate, + requestedMinimalPostProcessing, inTraversal); } @Override diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 83fc9665f192a..f23ae6e2340c6 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -913,10 +913,12 @@ public class DisplayModeDirector { // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY] public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2; - // APP_REQUEST_MAX_REFRESH_RATE is used to for internal apps to limit the refresh + // APP_REQUEST_REFRESH_RATE_RANGE is used to for internal apps to limit the refresh // rate in certain cases, mostly to preserve power. - // It votes to [0, APP_REQUEST_MAX_REFRESH_RATE]. - public static final int PRIORITY_APP_REQUEST_MAX_REFRESH_RATE = 3; + // @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate + // @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate + // It votes to [preferredMinRefreshRate, preferredMaxRefreshRate]. + public static final int PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE = 3; // We split the app request into different priorities in case we can satisfy one desire // without the other. @@ -967,7 +969,7 @@ public class DisplayModeDirector { // The cutoff for the app request refresh rate range. Votes with priorities lower than this // value will not be considered when constructing the app request refresh rate range. public static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF = - PRIORITY_APP_REQUEST_MAX_REFRESH_RATE; + PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE; /** * A value signifying an invalid width or height in a vote. @@ -1035,8 +1037,8 @@ public class DisplayModeDirector { switch (priority) { case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE: return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE"; - case PRIORITY_APP_REQUEST_MAX_REFRESH_RATE: - return "PRIORITY_APP_REQUEST_MAX_REFRESH_RATE"; + case PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE: + return "PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE"; case PRIORITY_APP_REQUEST_SIZE: return "PRIORITY_APP_REQUEST_SIZE"; case PRIORITY_DEFAULT_REFRESH_RATE: @@ -1233,17 +1235,19 @@ public class DisplayModeDirector { final class AppRequestObserver { private final SparseArray mAppRequestedModeByDisplay; - private final SparseArray mAppPreferredMaxRefreshRateByDisplay; + private final SparseArray mAppPreferredRefreshRateRangeByDisplay; AppRequestObserver() { mAppRequestedModeByDisplay = new SparseArray<>(); - mAppPreferredMaxRefreshRateByDisplay = new SparseArray<>(); + mAppPreferredRefreshRateRangeByDisplay = new SparseArray<>(); } - public void setAppRequest(int displayId, int modeId, float requestedMaxRefreshRate) { + public void setAppRequest(int displayId, int modeId, float requestedMinRefreshRateRange, + float requestedMaxRefreshRateRange) { synchronized (mLock) { setAppRequestedModeLocked(displayId, modeId); - setAppPreferredMaxRefreshRateLocked(displayId, requestedMaxRefreshRate); + setAppPreferredRefreshRateRangeLocked(displayId, requestedMinRefreshRateRange, + requestedMaxRefreshRateRange); } } @@ -1272,26 +1276,36 @@ public class DisplayModeDirector { updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote); } - private void setAppPreferredMaxRefreshRateLocked(int displayId, - float requestedMaxRefreshRate) { + private void setAppPreferredRefreshRateRangeLocked(int displayId, + float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) { final Vote vote; - final Float requestedMaxRefreshRateVote = - requestedMaxRefreshRate > 0 - ? new Float(requestedMaxRefreshRate) : null; - if (Objects.equals(requestedMaxRefreshRateVote, - mAppPreferredMaxRefreshRateByDisplay.get(displayId))) { + + RefreshRateRange refreshRateRange = null; + if (requestedMinRefreshRateRange > 0 || requestedMaxRefreshRateRange > 0) { + float min = requestedMinRefreshRateRange; + float max = requestedMaxRefreshRateRange > 0 + ? requestedMaxRefreshRateRange : Float.POSITIVE_INFINITY; + refreshRateRange = new RefreshRateRange(min, max); + if (refreshRateRange.min == 0 && refreshRateRange.max == 0) { + // requestedMinRefreshRateRange/requestedMaxRefreshRateRange were invalid + refreshRateRange = null; + } + } + + if (Objects.equals(refreshRateRange, + mAppPreferredRefreshRateRangeByDisplay.get(displayId))) { return; } - if (requestedMaxRefreshRate > 0) { - mAppPreferredMaxRefreshRateByDisplay.put(displayId, requestedMaxRefreshRateVote); - vote = Vote.forRefreshRates(0, requestedMaxRefreshRate); + if (refreshRateRange != null) { + mAppPreferredRefreshRateRangeByDisplay.put(displayId, refreshRateRange); + vote = Vote.forRefreshRates(refreshRateRange.min, refreshRateRange.max); } else { - mAppPreferredMaxRefreshRateByDisplay.remove(displayId); + mAppPreferredRefreshRateRangeByDisplay.remove(displayId); vote = null; } synchronized (mLock) { - updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE, vote); + updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE, vote); } } @@ -1316,11 +1330,12 @@ public class DisplayModeDirector { final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i); pw.println(" " + id + " -> " + mode); } - pw.println(" mAppPreferredMaxRefreshRateByDisplay:"); - for (int i = 0; i < mAppPreferredMaxRefreshRateByDisplay.size(); i++) { - final int id = mAppPreferredMaxRefreshRateByDisplay.keyAt(i); - final Float refreshRate = mAppPreferredMaxRefreshRateByDisplay.valueAt(i); - pw.println(" " + id + " -> " + refreshRate); + pw.println(" mAppPreferredRefreshRateRangeByDisplay:"); + for (int i = 0; i < mAppPreferredRefreshRateRangeByDisplay.size(); i++) { + final int id = mAppPreferredRefreshRateRangeByDisplay.keyAt(i); + final RefreshRateRange refreshRateRange = + mAppPreferredRefreshRateRangeByDisplay.valueAt(i); + pw.println(" " + id + " -> " + refreshRateRange); } } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 62d8ace91834b..7d9971cc80137 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -914,6 +914,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mTmpApplySurfaceChangesTransactionState.preferredModeId = preferredModeId; } + final float preferredMinRefreshRate = getDisplayPolicy().getRefreshRatePolicy() + .getPreferredMinRefreshRate(w); + if (mTmpApplySurfaceChangesTransactionState.preferredMinRefreshRate == 0 + && preferredMinRefreshRate != 0) { + mTmpApplySurfaceChangesTransactionState.preferredMinRefreshRate = + preferredMinRefreshRate; + } + final float preferredMaxRefreshRate = getDisplayPolicy().getRefreshRatePolicy() .getPreferredMaxRefreshRate(w); if (mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate == 0 @@ -4321,6 +4329,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mLastHasContent, mTmpApplySurfaceChangesTransactionState.preferredRefreshRate, mTmpApplySurfaceChangesTransactionState.preferredModeId, + mTmpApplySurfaceChangesTransactionState.preferredMinRefreshRate, mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate, mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing, true /* inTraversal, must call performTraversalInTrans... below */); @@ -4611,6 +4620,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp public boolean preferMinimalPostProcessing; public float preferredRefreshRate; public int preferredModeId; + public float preferredMinRefreshRate; public float preferredMaxRefreshRate; void reset() { @@ -4620,6 +4630,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp preferMinimalPostProcessing = false; preferredRefreshRate = 0; preferredModeId = 0; + preferredMinRefreshRate = 0; preferredMaxRefreshRate = 0; } } diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java index deaf611446bc6..6bc42af2bb686 100644 --- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java +++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java @@ -154,6 +154,21 @@ class RefreshRatePolicy { return 0; } + float getPreferredMinRefreshRate(WindowState w) { + // If app is animating, it's not able to control refresh rate because we want the animation + // to run in default refresh rate. + if (w.isAnimating(TRANSITION | PARENTS)) { + return 0; + } + + // If app requests a certain refresh rate or mode, don't override it. + if (w.mAttrs.preferredDisplayModeId != 0) { + return 0; + } + + return w.mAttrs.preferredMinDisplayRefreshRate; + } + float getPreferredMaxRefreshRate(WindowState w) { // If app is animating, it's not able to control refresh rate because we want the animation // to run in default refresh rate. diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index cae6c863ab024..a205a1d167fed 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -900,10 +900,10 @@ public class DisplayModeDirectorTest { } @Test - public void testAppRequestMaxRefreshRate() { - // Confirm that the app max request range doesn't include flicker or min refresh rate + public void testAppRequestMinRefreshRate() { + // Confirm that the app min request range doesn't include flicker or min refresh rate // settings but does include everything else. - assertTrue(Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE + assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); Display.Mode[] modes = new Display.Mode[3]; @@ -936,7 +936,54 @@ public class DisplayModeDirectorTest { assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); - votes.put(Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE, Vote.forRefreshRates(0, 75)); + votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE, + Vote.forRefreshRates(75, Float.POSITIVE_INFINITY)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.appRequestRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75); + assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); + } + + @Test + public void testAppRequestMaxRefreshRate() { + // Confirm that the app max request range doesn't include flicker or min refresh rate + // settings but does include everything else. + assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE + >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); + + Display.Mode[] modes = new Display.Mode[3]; + modes[0] = new Display.Mode( + /*modeId=*/60, /*width=*/1000, /*height=*/1000, 60); + modes[1] = new Display.Mode( + /*modeId=*/75, /*width=*/1000, /*height=*/1000, 75); + modes[2] = new Display.Mode( + /*modeId=*/90, /*width=*/1000, /*height=*/1000, 90); + + DisplayModeDirector director = createDirectorFromModeArray(modes, modes[1]); + SparseArray votes = new SparseArray<>(); + SparseArray> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(DISPLAY_ID, votes); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching()); + votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE, Vote.forRefreshRates(60, 60)); + director.injectVotesByDisplay(votesByDisplay); + DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); + assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); + + votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, + Vote.forRefreshRates(90, Float.POSITIVE_INFINITY)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90f); + assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f); + assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f); + + votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE, Vote.forRefreshRates(0, 75)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75); @@ -948,7 +995,7 @@ public class DisplayModeDirectorTest { @Test public void testAppRequestObserver_modeId() { DisplayModeDirector director = createDirectorFromFpsRange(60, 90); - director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 0); + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 0, 0); Vote appRequestRefreshRate = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); @@ -969,11 +1016,11 @@ public class DisplayModeDirectorTest { assertThat(appRequestSize.height).isEqualTo(1000); assertThat(appRequestSize.width).isEqualTo(1000); - Vote appRequestMaxRefreshRate = - director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE); - assertNull(appRequestMaxRefreshRate); + Vote appRequestRefreshRateRange = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + assertNull(appRequestRefreshRateRange); - director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 90, 0); + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 90, 0, 0); appRequestRefreshRate = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); @@ -992,15 +1039,15 @@ public class DisplayModeDirectorTest { assertThat(appRequestSize.height).isEqualTo(1000); assertThat(appRequestSize.width).isEqualTo(1000); - appRequestMaxRefreshRate = - director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE); - assertNull(appRequestMaxRefreshRate); + appRequestRefreshRateRange = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + assertNull(appRequestRefreshRateRange); } @Test - public void testAppRequestObserver_maxRefreshRate() { + public void testAppRequestObserver_minRefreshRate() { DisplayModeDirector director = createDirectorFromFpsRange(60, 90); - director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 90); + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 60, 0); Vote appRequestRefreshRate = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); assertNull(appRequestRefreshRate); @@ -1008,15 +1055,16 @@ public class DisplayModeDirectorTest { Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); assertNull(appRequestSize); - Vote appRequestMaxRefreshRate = - director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE); - assertNotNull(appRequestMaxRefreshRate); - assertThat(appRequestMaxRefreshRate.refreshRateRange.min).isZero(); - assertThat(appRequestMaxRefreshRate.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); - assertThat(appRequestMaxRefreshRate.height).isEqualTo(INVALID_SIZE); - assertThat(appRequestMaxRefreshRate.width).isEqualTo(INVALID_SIZE); + Vote appRequestRefreshRateRange = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + assertNotNull(appRequestRefreshRateRange); + assertThat(appRequestRefreshRateRange.refreshRateRange.min) + .isWithin(FLOAT_TOLERANCE).of(60); + assertThat(appRequestRefreshRateRange.refreshRateRange.max).isAtLeast(90); + assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE); + assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE); - director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 60); + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 90, 0); appRequestRefreshRate = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); assertNull(appRequestRefreshRate); @@ -1024,19 +1072,74 @@ public class DisplayModeDirectorTest { appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); assertNull(appRequestSize); - appRequestMaxRefreshRate = - director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE); - assertNotNull(appRequestMaxRefreshRate); - assertThat(appRequestMaxRefreshRate.refreshRateRange.min).isZero(); - assertThat(appRequestMaxRefreshRate.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); - assertThat(appRequestMaxRefreshRate.height).isEqualTo(INVALID_SIZE); - assertThat(appRequestMaxRefreshRate.width).isEqualTo(INVALID_SIZE); + appRequestRefreshRateRange = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + assertNotNull(appRequestRefreshRateRange); + assertThat(appRequestRefreshRateRange.refreshRateRange.min) + .isWithin(FLOAT_TOLERANCE).of(90); + assertThat(appRequestRefreshRateRange.refreshRateRange.max).isAtLeast(90); + assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE); + assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE); } @Test - public void testAppRequestObserver_modeIdAndMaxRefreshRate() { + public void testAppRequestObserver_maxRefreshRate() { DisplayModeDirector director = createDirectorFromFpsRange(60, 90); - director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 90); + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 0, 90); + Vote appRequestRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); + assertNull(appRequestRefreshRate); + + Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); + assertNull(appRequestSize); + + Vote appRequestRefreshRateRange = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + assertNotNull(appRequestRefreshRateRange); + assertThat(appRequestRefreshRateRange.refreshRateRange.min).isZero(); + assertThat(appRequestRefreshRateRange.refreshRateRange.max) + .isWithin(FLOAT_TOLERANCE).of(90); + assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE); + assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE); + + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 0, 60); + appRequestRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); + assertNull(appRequestRefreshRate); + + appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); + assertNull(appRequestSize); + + appRequestRefreshRateRange = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + assertNotNull(appRequestRefreshRateRange); + assertThat(appRequestRefreshRateRange.refreshRateRange.min).isZero(); + assertThat(appRequestRefreshRateRange.refreshRateRange.max) + .isWithin(FLOAT_TOLERANCE).of(60); + assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE); + assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE); + } + + @Test + public void testAppRequestObserver_invalidRefreshRateRange() { + DisplayModeDirector director = createDirectorFromFpsRange(60, 90); + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 90, 60); + Vote appRequestRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); + assertNull(appRequestRefreshRate); + + Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); + assertNull(appRequestSize); + + Vote appRequestRefreshRateRange = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + assertNull(appRequestRefreshRateRange); + } + + @Test + public void testAppRequestObserver_modeIdAndRefreshRateRange() { + DisplayModeDirector director = createDirectorFromFpsRange(60, 90); + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 90, 90); Vote appRequestRefreshRate = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE); @@ -1056,13 +1159,15 @@ public class DisplayModeDirectorTest { assertThat(appRequestSize.height).isEqualTo(1000); assertThat(appRequestSize.width).isEqualTo(1000); - Vote appRequestMaxRefreshRate = - director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE); - assertNotNull(appRequestMaxRefreshRate); - assertThat(appRequestMaxRefreshRate.refreshRateRange.min).isZero(); - assertThat(appRequestMaxRefreshRate.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); - assertThat(appRequestMaxRefreshRate.height).isEqualTo(INVALID_SIZE); - assertThat(appRequestMaxRefreshRate.width).isEqualTo(INVALID_SIZE); + Vote appRequestRefreshRateRange = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE); + assertNotNull(appRequestRefreshRateRange); + assertThat(appRequestRefreshRateRange.refreshRateRange.max) + .isWithin(FLOAT_TOLERANCE).of(90); + assertThat(appRequestRefreshRateRange.refreshRateRange.max) + .isWithin(FLOAT_TOLERANCE).of(90); + assertThat(appRequestRefreshRateRange.height).isEqualTo(INVALID_SIZE); + assertThat(appRequestRefreshRateRange.width).isEqualTo(INVALID_SIZE); } @Test @@ -1161,7 +1266,7 @@ public class DisplayModeDirectorTest { assertThat(desiredSpecs.baseModeId).isEqualTo(55); votes.clear(); - votes.put(Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE, Vote.forRefreshRates(0, 52)); + votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE, Vote.forRefreshRates(0, 52)); votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(55)); votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); @@ -1172,7 +1277,7 @@ public class DisplayModeDirectorTest { assertThat(desiredSpecs.baseModeId).isEqualTo(55); votes.clear(); - votes.put(Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE, Vote.forRefreshRates(0, 58)); + votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE, Vote.forRefreshRates(0, 58)); votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, Vote.forBaseModeRefreshRate(55)); votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRefreshRates(0, 60)); diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java index e1696924dd3df..b41872bfa45f3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java @@ -84,10 +84,12 @@ public class RefreshRatePolicyTest extends WindowTestsBase { parcelLayoutParams(cameraUsingWindow); assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); mPolicy.addNonHighRefreshRatePackage("com.android.test"); assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); assertEquals(60, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); mPolicy.removeNonHighRefreshRatePackage("com.android.test"); assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); @@ -127,6 +129,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase { mPolicy.addNonHighRefreshRatePackage("com.android.test"); assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE); assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE); } @@ -143,6 +146,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase { mPolicy.addNonHighRefreshRatePackage("com.android.test"); assertEquals(0, mPolicy.getPreferredModeId(overrideWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMinRefreshRate(overrideWindow), FLOAT_TOLERANCE); assertEquals(0, mPolicy.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE); } @@ -156,6 +160,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase { mPolicy.addNonHighRefreshRatePackage("com.android.test"); assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); assertEquals(60, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); cameraUsingWindow.mActivityRecord.mSurfaceAnimator.startAnimation( @@ -163,6 +168,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase { false /* hidden */, ANIMATION_TYPE_APP_TRANSITION); assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMinRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); } @@ -173,8 +179,28 @@ public class RefreshRatePolicyTest extends WindowTestsBase { parcelLayoutParams(window); assertEquals(0, mPolicy.getPreferredModeId(window)); assertEquals(0, mPolicy.getPreferredRefreshRate(window), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMinRefreshRate(window), FLOAT_TOLERANCE); assertEquals(60, mPolicy.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE); + window.mActivityRecord.mSurfaceAnimator.startAnimation( + window.getPendingTransaction(), mock(AnimationAdapter.class), + false /* hidden */, ANIMATION_TYPE_APP_TRANSITION); + assertEquals(0, mPolicy.getPreferredModeId(window)); + assertEquals(0, mPolicy.getPreferredRefreshRate(window), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMinRefreshRate(window), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE); + } + + @Test + public void testAppMinRefreshRate() { + final WindowState window = createWindow(null, TYPE_BASE_APPLICATION, "window"); + window.mAttrs.preferredMinDisplayRefreshRate = 60f; + parcelLayoutParams(window); + assertEquals(0, mPolicy.getPreferredModeId(window)); + assertEquals(0, mPolicy.getPreferredRefreshRate(window), FLOAT_TOLERANCE); + assertEquals(60, mPolicy.getPreferredMinRefreshRate(window), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE); + window.mActivityRecord.mSurfaceAnimator.startAnimation( window.getPendingTransaction(), mock(AnimationAdapter.class), false /* hidden */, ANIMATION_TYPE_APP_TRANSITION); @@ -182,4 +208,15 @@ public class RefreshRatePolicyTest extends WindowTestsBase { assertEquals(0, mPolicy.getPreferredRefreshRate(window), FLOAT_TOLERANCE); assertEquals(0, mPolicy.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE); } + + @Test + public void testAppPreferredRefreshRate() { + final WindowState window = createWindow(null, TYPE_BASE_APPLICATION, "window"); + window.mAttrs.preferredRefreshRate = 60f; + parcelLayoutParams(window); + assertEquals(0, mPolicy.getPreferredModeId(window)); + assertEquals(60, mPolicy.getPreferredRefreshRate(window), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMinRefreshRate(window), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE); + } }