From 26c834522be4f02919c69eeb12a3ed1092f07ca4 Mon Sep 17 00:00:00 2001 From: Ady Abraham Date: Wed, 14 Apr 2021 13:32:23 -0700 Subject: [PATCH] Add preferredMaxDisplayRefreshRate Add a new private window attribute for allowing apps to specify the max display refresh rate. This is useful for use cases such as keyguard where the refresh rate should be limited to preserve power. In the next CLs the preferredDiplayModeId would be enabled for frame rate override (60-on-120) and the preferredMaxDisplayRefreshRate would be the alternative to control the display refresh rate (as opposed to the frame rate experienced by the app). Test: atest RefreshRatePolicyTest Test: atest DisplayModeDirectorTest Test: Launch camera app and observe refresh rate Bug: 183226498 Change-Id: I1c5e9f6047cbea4bfb581251b8dd2b9058b3e378 --- .../display/DisplayManagerInternal.java | 6 +- core/java/android/view/WindowManager.java | 8 + ...NotificationShadeWindowControllerImpl.java | 22 +-- .../server/display/DisplayManagerService.java | 13 +- .../server/display/DisplayModeDirector.java | 53 +++++- .../com/android/server/wm/DisplayContent.java | 23 ++- .../android/server/wm/RefreshRatePolicy.java | 33 +++- .../display/DisplayModeDirectorTest.java | 160 ++++++++++++++++++ .../server/wm/RefreshRatePolicyTest.java | 26 ++- 9 files changed, 299 insertions(+), 45 deletions(-) diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 2d58520a942e7..dce3fefff285e 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -190,6 +190,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 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 * display. This is true when there is at least one visible window that wants minimal post * processng on. @@ -197,8 +199,8 @@ public abstract class DisplayManagerInternal { * prior to call to performTraversalInTransactionFromWindowManager. */ public abstract void setDisplayProperties(int displayId, boolean hasContent, - float requestedRefreshRate, int requestedModeId, boolean requestedMinimalPostProcessing, - boolean inTraversal); + float requestedRefreshRate, int requestedModeId, 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 c32ab3a2d717e..c1e394d7456a2 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -3009,6 +3009,14 @@ public interface WindowManager extends ViewManager { */ public int preferredDisplayModeId; + /** + * The max display refresh rate while the window is in focus. + * + * This value is ignored if {@link #preferredDisplayModeId} is set. + * @hide + */ + public float preferredMaxDisplayRefreshRate; + /** * An internal annotation for flags that can be specified to {@link #systemUiVisibility} * and {@link #subtreeSystemUiVisibility}. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java index 388d72da808d9..ae018ba4fe766 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java @@ -61,7 +61,6 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; @@ -85,7 +84,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW private final LayoutParams mLpChanged; private final boolean mKeyguardScreenRotation; private final long mLockScreenDisplayTimeout; - private final Display.Mode mKeyguardDisplayMode; + private final float mKeyguardRefreshRate; private final KeyguardViewMediator mKeyguardViewMediator; private final KeyguardBypassController mKeyguardBypassController; private ViewGroup mNotificationShadeView; @@ -135,14 +134,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW // Running on the highest frame rate available can be expensive. // Let's specify a preferred refresh rate, and allow higher FPS only when we // know that we're not falsing (because we unlocked.) - int keyguardRefreshRate = context.getResources() + mKeyguardRefreshRate = context.getResources() .getInteger(R.integer.config_keyguardRefreshRate); - // Find supported display mode with the same resolution and requested refresh rate. - mKeyguardDisplayMode = Arrays.stream(supportedModes).filter(mode -> - (int) mode.getRefreshRate() == keyguardRefreshRate - && mode.getPhysicalWidth() == currentMode.getPhysicalWidth() - && mode.getPhysicalHeight() == currentMode.getPhysicalHeight()) - .findFirst().orElse(null); } /** @@ -273,16 +266,17 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW mLpChanged.privateFlags &= ~LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; } - if (mKeyguardDisplayMode != null) { + if (mKeyguardRefreshRate > 0) { boolean bypassOnKeyguard = mKeyguardBypassController.getBypassEnabled() && state.mStatusBarState == StatusBarState.KEYGUARD && !state.mKeyguardFadingAway && !state.mKeyguardGoingAway; if (state.mDozing || bypassOnKeyguard) { - mLpChanged.preferredDisplayModeId = mKeyguardDisplayMode.getModeId(); + mLpChanged.preferredMaxDisplayRefreshRate = mKeyguardRefreshRate; } else { - mLpChanged.preferredDisplayModeId = 0; + mLpChanged.preferredMaxDisplayRefreshRate = 0; } - Trace.setCounter("display_mode_id", mLpChanged.preferredDisplayModeId); + Trace.setCounter("display_max_refresh_rate", + (long) mLpChanged.preferredMaxDisplayRefreshRate); } } @@ -669,7 +663,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(TAG + ":"); - pw.println(" mKeyguardDisplayMode=" + mKeyguardDisplayMode); + pw.println(" mKeyguardRefreshRate=" + mKeyguardRefreshRate); pw.println(mCurrentState); if (mNotificationShadeView != null && mNotificationShadeView.getViewRootImpl() != null) { mNotificationShadeView.getViewRootImpl().dump(" ", pw); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index f75f3e1f2f79e..d4920f56a27e2 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1498,8 +1498,8 @@ public final class DisplayManagerService extends SystemService { } private void setDisplayPropertiesInternal(int displayId, boolean hasContent, - float requestedRefreshRate, int requestedModeId, boolean preferMinimalPostProcessing, - boolean inTraversal) { + float requestedRefreshRate, int requestedModeId, float requestedMaxRefreshRate, + boolean preferMinimalPostProcessing, boolean inTraversal) { synchronized (mSyncRoot) { final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId); if (display == null) { @@ -1523,8 +1523,8 @@ public final class DisplayManagerService extends SystemService { requestedModeId = display.getDisplayInfoLocked().findDefaultModeByRefreshRate( requestedRefreshRate).getModeId(); } - mDisplayModeDirector.getAppRequestObserver().setAppRequestedMode( - displayId, requestedModeId); + mDisplayModeDirector.getAppRequestObserver().setAppRequest( + displayId, requestedModeId, requestedMaxRefreshRate); if (display.getDisplayInfoLocked().minimalPostProcessingSupported) { boolean mppRequest = mMinimalPostProcessingAllowed && preferMinimalPostProcessing; @@ -3189,10 +3189,11 @@ public final class DisplayManagerService extends SystemService { @Override public void setDisplayProperties(int displayId, boolean hasContent, - float requestedRefreshRate, int requestedMode, + float requestedRefreshRate, int requestedMode, float requestedMaxRefreshRate, boolean requestedMinimalPostProcessing, boolean inTraversal) { setDisplayPropertiesInternal(displayId, hasContent, requestedRefreshRate, - requestedMode, requestedMinimalPostProcessing, inTraversal); + requestedMode, 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 67779a2e16854..8095919ded082 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -922,6 +922,11 @@ 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 + // 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; + // We split the app request into different priorities in case we can satisfy one desire // without the other. @@ -930,19 +935,19 @@ public class DisplayModeDirector { // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId // System also forces some apps like denylisted app to run at a lower refresh rate. // @see android.R.array#config_highRefreshRateBlacklist - public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 3; - public static final int PRIORITY_APP_REQUEST_SIZE = 4; + public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 4; + public static final int PRIORITY_APP_REQUEST_SIZE = 5; // SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest // of low priority voters. It votes [0, max(PEAK, MIN)] - public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 5; + public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 6; // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on. - public static final int PRIORITY_LOW_POWER_MODE = 6; + public static final int PRIORITY_LOW_POWER_MODE = 7; // The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order // to function, so this needs to be the highest priority of all votes. - public static final int PRIORITY_UDFPS = 7; + public static final int PRIORITY_UDFPS = 8; // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString. @@ -953,7 +958,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_REFRESH_RATE; + PRIORITY_APP_REQUEST_MAX_REFRESH_RATE; /** * A value signifying an invalid width or height in a vote. @@ -997,6 +1002,8 @@ public class DisplayModeDirector { return "PRIORITY_FLICKER"; case PRIORITY_USER_SETTING_MIN_REFRESH_RATE: return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE"; + case PRIORITY_APP_REQUEST_MAX_REFRESH_RATE: + return "PRIORITY_APP_REQUEST_MAX_REFRESH_RATE"; case PRIORITY_APP_REQUEST_REFRESH_RATE: return "PRIORITY_APP_REQUEST_REFRESH_RATE"; case PRIORITY_APP_REQUEST_SIZE: @@ -1182,14 +1189,17 @@ public class DisplayModeDirector { final class AppRequestObserver { private final SparseArray mAppRequestedModeByDisplay; + private final SparseArray mAppPreferredMaxRefreshRateByDisplay; AppRequestObserver() { mAppRequestedModeByDisplay = new SparseArray<>(); + mAppPreferredMaxRefreshRateByDisplay = new SparseArray<>(); } - public void setAppRequestedMode(int displayId, int modeId) { + public void setAppRequest(int displayId, int modeId, float requestedMaxRefreshRate) { synchronized (mLock) { setAppRequestedModeLocked(displayId, modeId); + setAppPreferredMaxRefreshRateLocked(displayId, requestedMaxRefreshRate); } } @@ -1217,6 +1227,29 @@ public class DisplayModeDirector { updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote); } + private void setAppPreferredMaxRefreshRateLocked(int displayId, + float requestedMaxRefreshRate) { + final Vote vote; + final Float requestedMaxRefreshRateVote = + requestedMaxRefreshRate > 0 + ? new Float(requestedMaxRefreshRate) : null; + if (Objects.equals(requestedMaxRefreshRateVote, + mAppPreferredMaxRefreshRateByDisplay.get(displayId))) { + return; + } + + if (requestedMaxRefreshRate > 0) { + mAppPreferredMaxRefreshRateByDisplay.put(displayId, requestedMaxRefreshRateVote); + vote = Vote.forRefreshRates(0, requestedMaxRefreshRate); + } else { + mAppPreferredMaxRefreshRateByDisplay.remove(displayId); + vote = null; + } + synchronized (mLock) { + updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE, vote); + } + } + private Display.Mode findModeByIdLocked(int displayId, int modeId) { Display.Mode[] modes = mSupportedModesByDisplay.get(displayId); if (modes == null) { @@ -1238,6 +1271,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); + } } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index a10847876ab6a..ec79fd5617f54 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -901,6 +901,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp && preferredModeId != 0) { mTmpApplySurfaceChangesTransactionState.preferredModeId = preferredModeId; } + + final float preferredMaxRefreshRate = getDisplayPolicy().getRefreshRatePolicy() + .getPreferredMaxRefreshRate(w); + if (mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate == 0 + && preferredMaxRefreshRate != 0) { + mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate = + preferredMaxRefreshRate; + } } } @@ -4229,6 +4237,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mLastHasContent, mTmpApplySurfaceChangesTransactionState.preferredRefreshRate, mTmpApplySurfaceChangesTransactionState.preferredModeId, + mTmpApplySurfaceChangesTransactionState.preferredMaxRefreshRate, mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing, true /* inTraversal, must call performTraversalInTrans... below */); } @@ -4512,12 +4521,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } private static final class ApplySurfaceChangesTransactionState { - boolean displayHasContent; - boolean obscured; - boolean syswin; - boolean preferMinimalPostProcessing; - float preferredRefreshRate; - int preferredModeId; + public boolean displayHasContent; + public boolean obscured; + public boolean syswin; + public boolean preferMinimalPostProcessing; + public float preferredRefreshRate; + public int preferredModeId; + public float preferredMaxRefreshRate; void reset() { displayHasContent = false; @@ -4526,6 +4536,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp preferMinimalPostProcessing = false; preferredRefreshRate = 0; preferredModeId = 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 26871d130fbf0..b143012581be4 100644 --- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java +++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java @@ -95,18 +95,10 @@ class RefreshRatePolicy { return 0; } - // If app requests a certain refresh rate or mode, don't override it. if (w.mAttrs.preferredRefreshRate != 0 || w.mAttrs.preferredDisplayModeId != 0) { return w.mAttrs.preferredDisplayModeId; } - final String packageName = w.getOwningPackage(); - - // If app is using Camera, force it to default (lower) refresh rate. - if (mNonHighRefreshRatePackages.contains(packageName)) { - return mLowRefreshRateMode.getModeId(); - } - return 0; } @@ -147,4 +139,29 @@ class RefreshRatePolicy { } return 0; } + + 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. + if (w.isAnimating(TRANSITION | PARENTS)) { + return 0; + } + + // If the app set a preferredDisplayModeId, we ignore the max preferred refresh rate + if (w.mAttrs.preferredDisplayModeId != 0) { + return 0; + } + + if (w.mAttrs.preferredMaxDisplayRefreshRate > 0) { + return w.mAttrs.preferredMaxDisplayRefreshRate; + } + + final String packageName = w.getOwningPackage(); + // If app is using Camera, force it to default (lower) refresh rate. + if (mNonHighRefreshRatePackages.contains(packageName)) { + return mLowRefreshRateMode.getRefreshRate(); + } + + return 0; + } } 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 cda659f05adf0..cd0a6c6535613 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -23,10 +23,12 @@ import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REF import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE; +import static com.android.server.display.DisplayModeDirector.Vote.INVALID_SIZE; import static com.android.server.display.DisplayModeDirector.Vote.PRIORITY_FLICKER; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; @@ -724,6 +726,164 @@ public class DisplayModeDirectorTest { assertNull(vote); } + @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_MAX_REFRESH_RATE + >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); + + DisplayModeDirector director = createDirectorFromFpsRange(60, 90); + SparseArray votes = new SparseArray<>(); + SparseArray> votesByDisplay = new SparseArray<>(); + votesByDisplay.put(DISPLAY_ID, votes); + votes.put(PRIORITY_FLICKER, 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_MAX_REFRESH_RATE, Vote.forRefreshRates(0, 75)); + director.injectVotesByDisplay(votesByDisplay); + desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID); + // TODO(b/183226498): ideally the refresh rate range should be a single value since + // PRIORITY_FLICKER voted, however since PRIORITY_APP_REQUEST_MAX_REFRESH_RATE votes for + // range, PRIORITY_FLICKER is ignored. The same thing would happen with + // PRIORITY_LOW_POWER_MODE so this is not a regression. This will be fixed in a subsequent + // CL. + //assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75); + assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75); + //assertThat(desiredSpecs.appRequestRefreshRateRange.min) + // .isWithin(FLOAT_TOLERANCE) + // .of(75); + assertThat(desiredSpecs.appRequestRefreshRateRange.max) + .isWithin(FLOAT_TOLERANCE) + .of(75); + } + + @Test + public void testAppRequestObserver_modeId() { + DisplayModeDirector director = createDirectorFromFpsRange(60, 90); + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 0); + + Vote appRequestRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE); + assertNotNull(appRequestRefreshRate); + assertThat(appRequestRefreshRate.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(appRequestRefreshRate.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE); + assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE); + + Vote appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); + assertNotNull(appRequestSize); + assertThat(appRequestSize.refreshRateRange.min).isZero(); + assertThat(appRequestSize.refreshRateRange.max).isPositiveInfinity(); + 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); + + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 90, 0); + + appRequestRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE); + assertNotNull(appRequestRefreshRate); + assertThat(appRequestRefreshRate.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(appRequestRefreshRate.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90); + assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE); + assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE); + + appRequestSize = director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); + assertNotNull(appRequestSize); + assertThat(appRequestSize.refreshRateRange.min).isZero(); + assertThat(appRequestSize.refreshRateRange.max).isPositiveInfinity(); + assertThat(appRequestSize.height).isEqualTo(1000); + assertThat(appRequestSize.width).isEqualTo(1000); + + appRequestMaxRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE); + assertNull(appRequestMaxRefreshRate); + } + + @Test + public void testAppRequestObserver_maxRefreshRate() { + DisplayModeDirector director = createDirectorFromFpsRange(60, 90); + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 90); + Vote appRequestRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE); + assertNull(appRequestRefreshRate); + + 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); + + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 60); + appRequestRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE); + assertNull(appRequestRefreshRate); + + 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); + } + + @Test + public void testAppRequestObserver_modeIdAndMaxRefreshRate() { + DisplayModeDirector director = createDirectorFromFpsRange(60, 90); + director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 90); + + Vote appRequestRefreshRate = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_REFRESH_RATE); + assertNotNull(appRequestRefreshRate); + assertThat(appRequestRefreshRate.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(appRequestRefreshRate.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60); + assertThat(appRequestRefreshRate.height).isEqualTo(INVALID_SIZE); + assertThat(appRequestRefreshRate.width).isEqualTo(INVALID_SIZE); + + Vote appRequestSize = + director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_SIZE); + assertNotNull(appRequestSize); + assertThat(appRequestSize.refreshRateRange.min).isZero(); + assertThat(appRequestSize.refreshRateRange.max).isPositiveInfinity(); + 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); + } + private void assertVoteForRefreshRate(Vote vote, float refreshRate) { assertThat(vote).isNotNull(); final DisplayModeDirector.RefreshRateRange expectedRange = 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 ef3c7ae91fedc..20b987de9aa14 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java @@ -71,9 +71,11 @@ public class RefreshRatePolicyTest extends WindowTestsBase { cameraUsingWindow.mAttrs.packageName = "com.android.test"; assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); mPolicy.addNonHighRefreshRatePackage("com.android.test"); - assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow)); + assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(60, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); mPolicy.removeNonHighRefreshRatePackage("com.android.test"); assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); @@ -109,6 +111,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.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE); } @Test @@ -123,6 +126,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.getPreferredMaxRefreshRate(overrideWindow), FLOAT_TOLERANCE); } @Test @@ -132,13 +136,31 @@ public class RefreshRatePolicyTest extends WindowTestsBase { cameraUsingWindow.mAttrs.packageName = "com.android.test"; mPolicy.addNonHighRefreshRatePackage("com.android.test"); - assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow)); + assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(60, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); cameraUsingWindow.mActivityRecord.mSurfaceAnimator.startAnimation( cameraUsingWindow.getPendingTransaction(), mock(AnimationAdapter.class), false /* hidden */, ANIMATION_TYPE_APP_TRANSITION); assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow)); assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + assertEquals(0, mPolicy.getPreferredMaxRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE); + } + + @Test + public void testAppMaxRefreshRate() { + final WindowState window = createWindow(null, TYPE_BASE_APPLICATION, "window"); + window.mAttrs.preferredMaxDisplayRefreshRate = 60f; + assertEquals(0, mPolicy.getPreferredModeId(window)); + assertEquals(0, mPolicy.getPreferredRefreshRate(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.getPreferredMaxRefreshRate(window), FLOAT_TOLERANCE); } }