Merge "Use setFrameRate for high refresh rate deny list" into sc-dev

This commit is contained in:
Ady Abraham
2021-01-28 02:17:43 +00:00
committed by Android (Google) Code Review
8 changed files with 160 additions and 22 deletions

View File

@@ -547,16 +547,17 @@ public final class DisplayInfo implements Parcelable {
* Returns the id of the "default" mode with the given refresh rate, or {@code 0} if no suitable
* mode could be found.
*/
public int findDefaultModeByRefreshRate(float refreshRate) {
@Nullable
public Display.Mode findDefaultModeByRefreshRate(float refreshRate) {
Display.Mode[] modes = supportedModes;
Display.Mode defaultMode = getDefaultMode();
for (int i = 0; i < modes.length; i++) {
if (modes[i].matches(
defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), refreshRate)) {
return modes[i].getModeId();
return modes[i];
}
}
return 0;
return null;
}
/**

View File

@@ -217,6 +217,15 @@ public class Surface implements Parcelable {
*/
public static final int FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1;
/**
* This surface belongs to an app on the High Refresh Rate Deny list, and needs the display
* to operate at the exact frame rate.
*
* This is used internally by the platform and should not be used by apps.
* @hide
*/
public static final int FRAME_RATE_COMPATIBILITY_EXACT = 100;
/**
* Create an empty surface, which will later be filled in by readFromParcel().
* @hide

View File

@@ -1357,7 +1357,7 @@ public final class DisplayManagerService extends SystemService {
// Scan supported modes returned by display.getInfo() to find a mode with the same
// size as the default display mode but with the specified refresh rate instead.
requestedModeId = display.getDisplayInfoLocked().findDefaultModeByRefreshRate(
requestedRefreshRate);
requestedRefreshRate).getModeId();
}
mDisplayModeDirector.getAppRequestObserver().setAppRequestedMode(
displayId, requestedModeId);

View File

@@ -28,7 +28,7 @@ import android.view.DisplayInfo;
*/
class RefreshRatePolicy {
private final int mLowRefreshRateId;
private final Mode mLowRefreshRateMode;
private final ArraySet<String> mNonHighRefreshRatePackages = new ArraySet<>();
private final HighRefreshRateDenylist mHighRefreshRateDenylist;
private final WindowManagerService mWmService;
@@ -56,7 +56,7 @@ class RefreshRatePolicy {
RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
HighRefreshRateDenylist denylist) {
mLowRefreshRateId = findLowRefreshRateModeId(displayInfo);
mLowRefreshRateMode = findLowRefreshRateMode(displayInfo);
mHighRefreshRateDenylist = denylist;
mWmService = wmService;
}
@@ -65,7 +65,7 @@ class RefreshRatePolicy {
* Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the
* default mode.
*/
private int findLowRefreshRateModeId(DisplayInfo displayInfo) {
private Mode findLowRefreshRateMode(DisplayInfo displayInfo) {
Mode mode = displayInfo.getDefaultMode();
float[] refreshRates = displayInfo.getDefaultRefreshRates();
float bestRefreshRate = mode.getRefreshRate();
@@ -104,13 +104,9 @@ class RefreshRatePolicy {
// If app is using Camera, force it to default (lower) refresh rate.
if (mNonHighRefreshRatePackages.contains(packageName)) {
return mLowRefreshRateId;
return mLowRefreshRateMode.getModeId();
}
// If app is denylisted using higher refresh rate, return default (lower) refresh rate
if (mHighRefreshRateDenylist.isDenylisted(packageName)) {
return mLowRefreshRateId;
}
return 0;
}
@@ -137,4 +133,18 @@ class RefreshRatePolicy {
}
return LAYER_PRIORITY_UNSET;
}
float getPreferredRefreshRate(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;
}
final String packageName = w.getOwningPackage();
if (mHighRefreshRateDenylist.isDenylisted(packageName)) {
return mLowRefreshRateMode.getRefreshRate();
}
return 0;
}
}

View File

@@ -233,6 +233,7 @@ import android.view.InputWindowHandle;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.Surface;
import android.view.Surface.Rotation;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -730,6 +731,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET;
/**
* This is the frame rate which is passed to SurfaceFlinger if the window is part of the
* high refresh rate deny list. The variable is cached, so we do not send too many updates to
* SF.
*/
float mDenyListFrameRate = 0f;
static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */
private final WindowProcessController mWpcForDisplayAreaConfigChanges;
@@ -5233,7 +5241,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 && mOwnerCanUseBackgroundBlur;
}
/**
* Notifies SF about the priority of the window, if it changed. SF then uses this information
* to decide which window's desired rendering rate should have a priority when deciding about
@@ -5242,13 +5249,21 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
*/
@VisibleForTesting
void updateFrameRateSelectionPriorityIfNeeded() {
final int priority = getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
.calculatePriority(this);
RefreshRatePolicy refreshRatePolicy =
getDisplayContent().getDisplayPolicy().getRefreshRatePolicy();
final int priority = refreshRatePolicy.calculatePriority(this);
if (mFrameRateSelectionPriority != priority) {
mFrameRateSelectionPriority = priority;
getPendingTransaction().setFrameRateSelectionPriority(mSurfaceControl,
mFrameRateSelectionPriority);
}
final float refreshRate = refreshRatePolicy.getPreferredRefreshRate(this);
if (mDenyListFrameRate != refreshRate) {
mDenyListFrameRate = refreshRate;
getPendingTransaction().setFrameRate(
mSurfaceControl, mDenyListFrameRate, Surface.FRAME_RATE_COMPATIBILITY_EXACT);
}
}
private void updateGlobalScaleIfNeeded() {

View File

@@ -18,15 +18,24 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.platform.test.annotations.Presubmit;
import android.view.Display.Mode;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,16 +49,40 @@ import org.junit.runner.RunWith;
@Presubmit
@RunWith(WindowTestRunner.class)
public class FrameRateSelectionPriorityTests extends WindowTestsBase {
private static final float FLOAT_TOLERANCE = 0.01f;
private static final int LOW_MODE_ID = 3;
private DisplayPolicy mDisplayPolicy = mock(DisplayPolicy.class);
private RefreshRatePolicy mRefreshRatePolicy;
private HighRefreshRateDenylist mDenylist = mock(HighRefreshRateDenylist.class);
@Before
public void setUp() {
DisplayInfo di = new DisplayInfo(mDisplayInfo);
Mode defaultMode = di.getDefaultMode();
di.supportedModes = new Mode[] {
new Mode(1, defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 90),
new Mode(2, defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 70),
new Mode(LOW_MODE_ID,
defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 60),
};
di.defaultModeId = 1;
mRefreshRatePolicy = new RefreshRatePolicy(mWm, di, mDenylist);
when(mDisplayPolicy.getRefreshRatePolicy()).thenReturn(mRefreshRatePolicy);
}
@Test
public void basicTest() {
final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
assertNotNull("Window state is created", appWindow);
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority doesn't change.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
// Call the function a few times.
appWindow.updateFrameRateSelectionPriorityIfNeeded();
@@ -57,7 +90,9 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
// Since nothing changed in the priority state, the transaction should not be updating.
verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
any(SurfaceControl.class), anyInt());
verify(appWindow.getPendingTransaction(), never()).setFrameRate(
any(SurfaceControl.class), anyInt(), anyInt());
}
@Test
@@ -66,10 +101,16 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
.getPreferredModeId(appWindow), 0);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
.getPreferredRefreshRate(appWindow), 0, FLOAT_TOLERANCE);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority stays MAX_VALUE.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
@@ -78,31 +119,38 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority changes to 1.
assertEquals(appWindow.mFrameRateSelectionPriority, 1);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), 1);
verify(appWindow.getPendingTransaction(), never()).setFrameRate(
any(SurfaceControl.class), anyInt(), anyInt());
}
@Test
public void testApplicationInFocusWithModeId() {
final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
// Application is in focus.
appWindow.mToken.mDisplayContent.mCurrentFocus = appWindow;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority changes.
assertEquals(appWindow.mFrameRateSelectionPriority, 1);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
// Update the mode ID to a requested number.
appWindow.mAttrs.preferredDisplayModeId = 1;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority changes.
assertEquals(appWindow.mFrameRateSelectionPriority, 0);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
// Remove the mode ID request.
appWindow.mAttrs.preferredDisplayModeId = 0;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority changes.
assertEquals(appWindow.mFrameRateSelectionPriority, 1);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
// Verify we called actions on Transactions correctly.
verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
@@ -111,12 +159,15 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
appWindow.getSurfaceControl(), 0);
verify(appWindow.getPendingTransaction(), times(2)).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), 1);
verify(appWindow.getPendingTransaction(), never()).setFrameRate(
any(SurfaceControl.class), anyInt(), anyInt());
}
@Test
public void testApplicationNotInFocusWithModeId() {
final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus");
appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
@@ -124,23 +175,28 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// The window is not in focus.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
// Update the mode ID to a requested number.
appWindow.mAttrs.preferredDisplayModeId = 1;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority changes.
assertEquals(appWindow.mFrameRateSelectionPriority, 2);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), 2);
verify(appWindow.getPendingTransaction(), never()).setFrameRate(
any(SurfaceControl.class), anyInt(), anyInt());
}
@Test
public void testApplicationNotInFocusWithoutModeId() {
final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus");
appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
@@ -148,14 +204,45 @@ public class FrameRateSelectionPriorityTests extends WindowTestsBase {
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// The window is not in focus.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
// Make sure that the mode ID is not set.
appWindow.mAttrs.preferredDisplayModeId = 0;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority doesn't change.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
verify(appWindow.getPendingTransaction(), never()).setFrameRate(
any(SurfaceControl.class), anyInt(), anyInt());
}
@Test
public void testPreferredRefreshRate() {
final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
assertNotNull("Window state is created", appWindow);
when(appWindow.getDisplayContent().getDisplayPolicy()).thenReturn(mDisplayPolicy);
appWindow.mAttrs.packageName = "com.android.test";
when(mDenylist.isDenylisted("com.android.test")).thenReturn(true);
assertEquals(0, mRefreshRatePolicy.getPreferredModeId(appWindow));
assertEquals(60, mRefreshRatePolicy.getPreferredRefreshRate(appWindow), FLOAT_TOLERANCE);
appWindow.updateFrameRateSelectionPriorityIfNeeded();
assertEquals(RefreshRatePolicy.LAYER_PRIORITY_UNSET, appWindow.mFrameRateSelectionPriority);
assertEquals(60, appWindow.mDenyListFrameRate, FLOAT_TOLERANCE);
// Call the function a few times.
appWindow.updateFrameRateSelectionPriorityIfNeeded();
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Since nothing changed in the priority state, the transaction should not be updating.
verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
any(SurfaceControl.class), anyInt());
verify(appWindow.getPendingTransaction(), times(1)).setFrameRate(
appWindow.getSurfaceControl(), 60, Surface.FRAME_RATE_COMPATIBILITY_EXACT);
}
}

View File

@@ -44,7 +44,7 @@ import org.junit.runner.RunWith;
@RunWith(WindowTestRunner.class)
@FlakyTest
public class RefreshRatePolicyTest extends WindowTestsBase {
private static final float FLOAT_TOLERANCE = 0.01f;
private static final int LOW_MODE_ID = 3;
private RefreshRatePolicy mPolicy;
@@ -70,28 +70,34 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
"cameraUsingWindow");
cameraUsingWindow.mAttrs.packageName = "com.android.test";
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
mPolicy.removeNonHighRefreshRatePackage("com.android.test");
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
}
@Test
public void testBlacklist() {
final WindowState blacklistedWindow = createWindow(null, TYPE_BASE_APPLICATION,
"blacklistedWindow");
blacklistedWindow.mAttrs.packageName = "com.android.test";
public void testDenyList() {
final WindowState denylistedWindow = createWindow(null, TYPE_BASE_APPLICATION,
"denylistedWindow");
denylistedWindow.mAttrs.packageName = "com.android.test";
when(mDenylist.isDenylisted("com.android.test")).thenReturn(true);
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(blacklistedWindow));
assertEquals(0, mPolicy.getPreferredModeId(denylistedWindow));
assertEquals(60, mPolicy.getPreferredRefreshRate(denylistedWindow), FLOAT_TOLERANCE);
}
@Test
public void testAppOverride_blacklist() {
final WindowState overrideWindow = createWindow(null, TYPE_BASE_APPLICATION,
"overrideWindow");
overrideWindow.mAttrs.packageName = "com.android.test";
overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID;
when(mDenylist.isDenylisted("com.android.test")).thenReturn(true);
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
assertEquals(60, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
}
@Test
@@ -102,6 +108,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID;
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
}
@Test
@@ -115,6 +122,7 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(0, mPolicy.getPreferredModeId(overrideWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
}
@Test
@@ -125,10 +133,12 @@ public class RefreshRatePolicyTest extends WindowTestsBase {
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow));
assertEquals(0, mPolicy.getPreferredRefreshRate(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);
}
}

View File

@@ -244,6 +244,12 @@ public class StubTransaction extends SurfaceControl.Transaction {
return this;
}
@Override
public SurfaceControl.Transaction setFrameRate(SurfaceControl sc, float frameRate,
int compatibility) {
return this;
}
@Override
public SurfaceControl.Transaction unsetColor(SurfaceControl sc) {
return this;