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); } }