Properly handling situation when display is added and removed instantly

Bug: b/279146324 b/279145472 b/270742628
Test: atest DisplayModeDirectorTest atest ActivityThreadTest atest LogicalDisplayTest
Change-Id: Ic48d67f4c1e383ca1419eb3980625a61ce766a7c
This commit is contained in:
Oleg Petsjonkin
2023-04-21 12:20:57 +00:00
committed by Oleg Petšjonkin
parent 3d3fc71b21
commit d6795179ab
7 changed files with 173 additions and 52 deletions

View File

@@ -341,6 +341,9 @@ public final class DisplayInfo implements Parcelable {
@Nullable
public DisplayShape displayShape;
/**
* Refresh rate range limitation based on the current device layout
*/
@Nullable
public SurfaceControl.RefreshRateRange layoutLimitedRefreshRate;
@@ -354,7 +357,7 @@ public final class DisplayInfo implements Parcelable {
* RefreshRateRange limitation for @Temperature.ThrottlingStatus
*/
@NonNull
public SparseArray<SurfaceControl.RefreshRateRange> refreshRateThermalThrottling =
public SparseArray<SurfaceControl.RefreshRateRange> thermalRefreshRateThrottling =
new SparseArray<>();
public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
@@ -434,7 +437,7 @@ public final class DisplayInfo implements Parcelable {
&& Objects.equals(displayShape, other.displayShape)
&& Objects.equals(layoutLimitedRefreshRate, other.layoutLimitedRefreshRate)
&& BrightnessSynchronizer.floatEquals(hdrSdrRatio, other.hdrSdrRatio)
&& refreshRateThermalThrottling.contentEquals(other.refreshRateThermalThrottling);
&& thermalRefreshRateThrottling.contentEquals(other.thermalRefreshRateThrottling);
}
@Override
@@ -491,7 +494,7 @@ public final class DisplayInfo implements Parcelable {
displayShape = other.displayShape;
layoutLimitedRefreshRate = other.layoutLimitedRefreshRate;
hdrSdrRatio = other.hdrSdrRatio;
refreshRateThermalThrottling = other.refreshRateThermalThrottling;
thermalRefreshRateThrottling = other.thermalRefreshRateThrottling;
}
public void readFromParcel(Parcel source) {
@@ -554,7 +557,7 @@ public final class DisplayInfo implements Parcelable {
displayShape = source.readTypedObject(DisplayShape.CREATOR);
layoutLimitedRefreshRate = source.readTypedObject(SurfaceControl.RefreshRateRange.CREATOR);
hdrSdrRatio = source.readFloat();
refreshRateThermalThrottling = source.readSparseArray(null,
thermalRefreshRateThrottling = source.readSparseArray(null,
SurfaceControl.RefreshRateRange.class);
}
@@ -616,7 +619,7 @@ public final class DisplayInfo implements Parcelable {
dest.writeTypedObject(displayShape, flags);
dest.writeTypedObject(layoutLimitedRefreshRate, flags);
dest.writeFloat(hdrSdrRatio);
dest.writeSparseArray(refreshRateThermalThrottling);
dest.writeSparseArray(thermalRefreshRateThrottling);
}
@Override
@@ -884,8 +887,8 @@ public final class DisplayInfo implements Parcelable {
} else {
sb.append(hdrSdrRatio);
}
sb.append(", refreshRateThermalThrottling ");
sb.append(refreshRateThermalThrottling);
sb.append(", thermalRefreshRateThrottling ");
sb.append(thermalRefreshRateThrottling);
sb.append("}");
return sb.toString();
}

View File

@@ -181,6 +181,19 @@ final class LogicalDisplay {
*/
private String mThermalBrightnessThrottlingDataId;
/**
* Refresh rate range limitation based on the current device layout
*/
@Nullable
private SurfaceControl.RefreshRateRange mLayoutLimitedRefreshRate;
/**
* RefreshRateRange limitation for @Temperature.ThrottlingStatus
*/
@NonNull
private SparseArray<SurfaceControl.RefreshRateRange> mThermalRefreshRateThrottling =
new SparseArray<>();
public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
mDisplayId = displayId;
mLayerStack = layerStack;
@@ -339,24 +352,24 @@ final class LogicalDisplay {
*/
public void updateLayoutLimitedRefreshRateLocked(
@Nullable SurfaceControl.RefreshRateRange layoutLimitedRefreshRate) {
if (!Objects.equals(layoutLimitedRefreshRate, mBaseDisplayInfo.layoutLimitedRefreshRate)) {
mBaseDisplayInfo.layoutLimitedRefreshRate = layoutLimitedRefreshRate;
mInfo.set(null);
if (!Objects.equals(layoutLimitedRefreshRate, mLayoutLimitedRefreshRate)) {
mLayoutLimitedRefreshRate = layoutLimitedRefreshRate;
mDirty = true;
}
}
/**
* Updates refreshRateThermalThrottling
* Updates thermalRefreshRateThrottling
*
* @param refreshRanges new refreshRateThermalThrottling ranges limited by layout or default
* @param refreshRanges new thermalRefreshRateThrottling ranges limited by layout or default
*/
public void updateThermalRefreshRateThrottling(
@Nullable SparseArray<SurfaceControl.RefreshRateRange> refreshRanges) {
if (refreshRanges == null) {
refreshRanges = new SparseArray<>();
}
if (!mBaseDisplayInfo.refreshRateThermalThrottling.contentEquals(refreshRanges)) {
mBaseDisplayInfo.refreshRateThermalThrottling = refreshRanges;
mInfo.set(null);
if (!mThermalRefreshRateThrottling.contentEquals(refreshRanges)) {
mThermalRefreshRateThrottling = refreshRanges;
mDirty = true;
}
}
@@ -499,6 +512,9 @@ final class LogicalDisplay {
mBaseDisplayInfo.removeMode = Display.REMOVE_MODE_DESTROY_CONTENT;
}
mBaseDisplayInfo.layoutLimitedRefreshRate = mLayoutLimitedRefreshRate;
mBaseDisplayInfo.thermalRefreshRateThrottling = mThermalRefreshRateThrottling;
mPrimaryDisplayDeviceInfo = deviceInfo;
mInfo.set(null);
mDirty = false;
@@ -952,6 +968,8 @@ final class LogicalDisplay {
pw.println("mDisplayGroupName=" + mDisplayGroupName);
pw.println("mThermalBrightnessThrottlingDataId=" + mThermalBrightnessThrottlingDataId);
pw.println("mLeadDisplayId=" + mLeadDisplayId);
pw.println("mLayoutLimitedRefreshRate=" + mLayoutLimitedRefreshRate);
pw.println("mThermalRefreshRateThrottling=" + mThermalRefreshRateThrottling);
}
@Override

View File

@@ -1705,14 +1705,13 @@ public class DisplayModeDirector {
}
public void observe() {
DisplayManager dm = mContext.getSystemService(DisplayManager.class);
dm.registerDisplayListener(this, mHandler);
mInjector.registerDisplayListener(this, mHandler);
// Populate existing displays
SparseArray<Display.Mode[]> modes = new SparseArray<>();
SparseArray<Display.Mode> defaultModes = new SparseArray<>();
DisplayInfo info = new DisplayInfo();
Display[] displays = dm.getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
Display[] displays = mInjector.getDisplays();
for (Display d : displays) {
final int displayId = d.getDisplayId();
d.getDisplayInfo(info);
@@ -1753,16 +1752,9 @@ public class DisplayModeDirector {
@Nullable
private DisplayInfo getDisplayInfo(int displayId) {
Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
if (d == null) {
// We can occasionally get a display added or changed event for a display that was
// subsequently removed, which means this returns null. Check this case and bail
// out early; if it gets re-attached we'll eventually get another call back for it.
return null;
}
DisplayInfo info = new DisplayInfo();
d.getDisplayInfo(info);
return info;
// Display info might be invalid, in this case return null
return mInjector.getDisplayInfo(displayId, info) ? info : null;
}
private void updateLayoutLimitedFrameRate(int displayId, @Nullable DisplayInfo info) {
@@ -2432,8 +2424,7 @@ public class DisplayModeDirector {
}
private void updateDefaultDisplayState() {
Display display = mContext.getSystemService(DisplayManager.class)
.getDisplay(Display.DEFAULT_DISPLAY);
Display display = mInjector.getDisplay(Display.DEFAULT_DISPLAY);
if (display == null) {
return;
}
@@ -2750,8 +2741,7 @@ public class DisplayModeDirector {
sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this);
synchronized (mSensorObserverLock) {
for (Display d : mDisplayManager.getDisplays(
DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) {
for (Display d : mInjector.getDisplays()) {
mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d));
}
}
@@ -2762,8 +2752,7 @@ public class DisplayModeDirector {
}
private void recalculateVotesLocked() {
final Display[] displays = mDisplayManager.getDisplays(
DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
final Display[] displays = mInjector.getDisplays();
for (Display d : displays) {
int displayId = d.getDisplayId();
Vote vote = null;
@@ -2794,7 +2783,7 @@ public class DisplayModeDirector {
@Override
public void onDisplayAdded(int displayId) {
boolean isDozeState = mInjector.isDozeState(mDisplayManager.getDisplay(displayId));
boolean isDozeState = mInjector.isDozeState(mInjector.getDisplay(displayId));
synchronized (mSensorObserverLock) {
mDozeStateByDisplay.put(displayId, isDozeState);
recalculateVotesLocked();
@@ -2806,7 +2795,7 @@ public class DisplayModeDirector {
boolean wasDozeState = mDozeStateByDisplay.get(displayId);
synchronized (mSensorObserverLock) {
mDozeStateByDisplay.put(displayId,
mInjector.isDozeState(mDisplayManager.getDisplay(displayId)));
mInjector.isDozeState(mInjector.getDisplay(displayId)));
if (wasDozeState != mDozeStateByDisplay.get(displayId)) {
recalculateVotesLocked();
}
@@ -3175,9 +3164,14 @@ public class DisplayModeDirector {
void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer);
void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
Handler handler);
void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
Handler handler, long flags);
Display getDisplay(int displayId);
Display[] getDisplays();
boolean getDisplayInfo(int displayId, DisplayInfo displayInfo);
@@ -3220,12 +3214,23 @@ public class DisplayModeDirector {
observer, UserHandle.USER_SYSTEM);
}
@Override
public void registerDisplayListener(DisplayManager.DisplayListener listener,
Handler handler) {
getDisplayManager().registerDisplayListener(listener, handler);
}
@Override
public void registerDisplayListener(DisplayManager.DisplayListener listener,
Handler handler, long flags) {
getDisplayManager().registerDisplayListener(listener, handler, flags);
}
@Override
public Display getDisplay(int displayId) {
return getDisplayManager().getDisplay(displayId);
}
@Override
public Display[] getDisplays() {
return getDisplayManager().getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED);
@@ -3234,10 +3239,13 @@ public class DisplayModeDirector {
@Override
public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) {
Display display = getDisplayManager().getDisplay(displayId);
if (display != null) {
return display.getDisplayInfo(displayInfo);
if (display == null) {
// We can occasionally get a display added or changed event for a display that was
// subsequently removed, which means this returns null. Check this case and bail
// out early; if it gets re-attached we'll eventually get another call back for it.
return false;
}
return false;
return display.getDisplayInfo(displayInfo);
}
@Override

View File

@@ -138,7 +138,7 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
for (Display d : displays) {
final int displayId = d.getDisplayId();
d.getDisplayInfo(info);
localMap.put(displayId, info.refreshRateThermalThrottling);
localMap.put(displayId, info.thermalRefreshRateThrottling);
}
synchronized (mThermalObserverLock) {
for (int i = 0; i < size; i++) {
@@ -154,7 +154,7 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
DisplayInfo displayInfo = new DisplayInfo();
mInjector.getDisplayInfo(displayId, displayInfo);
SparseArray<SurfaceControl.RefreshRateRange> throttlingMap =
displayInfo.refreshRateThermalThrottling;
displayInfo.thermalRefreshRateThrottling;
synchronized (mThermalObserverLock) {
mThermalThrottlingByDisplay.put(displayId, throttlingMap);

View File

@@ -17,6 +17,8 @@
package com.android.server.display;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -26,6 +28,7 @@ import static org.mockito.Mockito.when;
import android.app.PropertyInvalidatedCache;
import android.graphics.Point;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -47,6 +50,7 @@ public class LogicalDisplayTest {
private static final int LAYER_STACK = 0;
private static final int DISPLAY_WIDTH = 100;
private static final int DISPLAY_HEIGHT = 200;
private static final int MODE_ID = 1;
private LogicalDisplay mLogicalDisplay;
private DisplayDevice mDisplayDevice;
@@ -65,6 +69,9 @@ public class LogicalDisplayTest {
mDisplayDeviceInfo.height = DISPLAY_HEIGHT;
mDisplayDeviceInfo.flags = DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
mDisplayDeviceInfo.modeId = MODE_ID;
mDisplayDeviceInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID,
DISPLAY_WIDTH, DISPLAY_HEIGHT, /* refreshRate= */ 60)};
when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(mDisplayDeviceInfo);
// Disable binder caches in this process.
@@ -168,14 +175,34 @@ public class LogicalDisplayTest {
}
@Test
public void testLayoutLimitedRefreshRateNotClearedAfterUpdate() {
SurfaceControl.RefreshRateRange refreshRateRange = new SurfaceControl.RefreshRateRange(1,
2);
mLogicalDisplay.updateLayoutLimitedRefreshRateLocked(refreshRateRange);
mLogicalDisplay.updateDisplayGroupIdLocked(1);
public void testUpdateLayoutLimitedRefreshRate() {
SurfaceControl.RefreshRateRange layoutLimitedRefreshRate =
new SurfaceControl.RefreshRateRange(0, 120);
DisplayInfo info1 = mLogicalDisplay.getDisplayInfoLocked();
mLogicalDisplay.updateLayoutLimitedRefreshRateLocked(layoutLimitedRefreshRate);
DisplayInfo info2 = mLogicalDisplay.getDisplayInfoLocked();
// Display info should only be updated when updateLocked is called
assertEquals(info2, info1);
DisplayInfo result = mLogicalDisplay.getDisplayInfoLocked();
mLogicalDisplay.updateLocked(mDeviceRepo);
DisplayInfo info3 = mLogicalDisplay.getDisplayInfoLocked();
assertNotEquals(info3, info2);
assertEquals(layoutLimitedRefreshRate, info3.layoutLimitedRefreshRate);
}
assertEquals(refreshRateRange, result.layoutLimitedRefreshRate);
@Test
public void testUpdateRefreshRateThermalThrottling() {
SparseArray<SurfaceControl.RefreshRateRange> refreshRanges = new SparseArray<>();
refreshRanges.put(0, new SurfaceControl.RefreshRateRange(0, 120));
DisplayInfo info1 = mLogicalDisplay.getDisplayInfoLocked();
mLogicalDisplay.updateThermalRefreshRateThrottling(refreshRanges);
DisplayInfo info2 = mLogicalDisplay.getDisplayInfoLocked();
// Display info should only be updated when updateLocked is called
assertEquals(info2, info1);
mLogicalDisplay.updateLocked(mDeviceRepo);
DisplayInfo info3 = mLogicalDisplay.getDisplayInfoLocked();
assertNotEquals(info3, info2);
assertTrue(refreshRanges.contentEquals(info3.thermalRefreshRateThrottling));
}
}

View File

@@ -127,7 +127,8 @@ public class DisplayModeDirectorTest {
private static final String TAG = "DisplayModeDirectorTest";
private static final boolean DEBUG = false;
private static final float FLOAT_TOLERANCE = 0.01f;
private static final int DISPLAY_ID = 0;
private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY;
private static final int MODE_ID = 1;
private static final float TRANSITION_POINT = 0.763f;
private static final float HBM_TRANSITION_POINT_INVALID = Float.POSITIVE_INFINITY;
@@ -2644,6 +2645,53 @@ public class DisplayModeDirectorTest {
assertNull(vote);
}
@Test
public void testUpdateLayoutLimitedRefreshRate_validDisplayInfo() {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
director.start(createMockSensorManager());
ArgumentCaptor<DisplayListener> displayListenerCaptor =
ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
any(Handler.class));
DisplayListener displayListener = displayListenerCaptor.getValue();
float refreshRate = 60;
mInjector.mDisplayInfo.layoutLimitedRefreshRate =
new RefreshRateRange(refreshRate, refreshRate);
displayListener.onDisplayChanged(DISPLAY_ID);
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
assertVoteForPhysicalRefreshRate(vote, /* refreshRate= */ refreshRate);
mInjector.mDisplayInfo.layoutLimitedRefreshRate = null;
displayListener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
assertNull(vote);
}
@Test
public void testUpdateLayoutLimitedRefreshRate_invalidDisplayInfo() {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
director.start(createMockSensorManager());
ArgumentCaptor<DisplayListener> displayListenerCaptor =
ArgumentCaptor.forClass(DisplayListener.class);
verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
any(Handler.class));
DisplayListener displayListener = displayListenerCaptor.getValue();
mInjector.mDisplayInfo.layoutLimitedRefreshRate = new RefreshRateRange(10, 10);
mInjector.mDisplayInfoValid = false;
displayListener.onDisplayChanged(DISPLAY_ID);
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE);
assertNull(vote);
}
private Temperature getSkinTemp(@Temperature.ThrottlingStatus int status) {
return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
}
@@ -2850,12 +2898,20 @@ public class DisplayModeDirectorTest {
public static class FakesInjector implements DisplayModeDirector.Injector {
private final FakeDeviceConfig mDeviceConfig;
private final DisplayInfo mDisplayInfo;
private final Display mDisplay;
private boolean mDisplayInfoValid = true;
private ContentObserver mBrightnessObserver;
private ContentObserver mSmoothDisplaySettingObserver;
private ContentObserver mForcePeakRefreshRateSettingObserver;
FakesInjector() {
mDeviceConfig = new FakeDeviceConfig();
mDisplayInfo = new DisplayInfo();
mDisplayInfo.defaultModeId = MODE_ID;
mDisplayInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID,
800, 600, /* refreshRate= */ 60)};
mDisplay = createDisplay(DISPLAY_ID);
}
@NonNull
@@ -2875,17 +2931,26 @@ public class DisplayModeDirectorTest {
mForcePeakRefreshRateSettingObserver = observer;
}
@Override
public void registerDisplayListener(DisplayListener listener, Handler handler) {}
@Override
public void registerDisplayListener(DisplayListener listener, Handler handler, long flag) {}
@Override
public Display getDisplay(int displayId) {
return mDisplay;
}
@Override
public Display[] getDisplays() {
return new Display[] { createDisplay(DISPLAY_ID) };
return new Display[] { mDisplay };
}
@Override
public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) {
return false;
displayInfo.copyFrom(mDisplayInfo);
return mDisplayInfoValid;
}
@Override
@@ -2909,7 +2974,7 @@ public class DisplayModeDirectorTest {
}
protected Display createDisplay(int id) {
return new Display(DisplayManagerGlobal.getInstance(), id, new DisplayInfo(),
return new Display(DisplayManagerGlobal.getInstance(), id, mDisplayInfo,
ApplicationProvider.getApplicationContext().getResources());
}

View File

@@ -253,7 +253,7 @@ public class SkinThermalStatusObserverTest {
public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) {
SparseArray<SurfaceControl.RefreshRateRange> config = mOverriddenConfig.get(displayId);
if (config != null) {
displayInfo.refreshRateThermalThrottling = config;
displayInfo.thermalRefreshRateThrottling = config;
return true;
}
return false;