Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9fc75134 authored by Piotr Wilczyński's avatar Piotr Wilczyński
Browse files

Min and max refresh rate per display (take 2)

- In onDisplayAdded/onDisplayChanged/onDisplayRemoved, update the min and peak refresh rates for this display.

- When Smooth Display or Force Peak Refresh Rate are turned on/off, update the min and peak refresh rates for all the displays.

- Fix the deadlock - use the supported modes by display already stored in DMD instead of getting them from DisplayManager (which acquires the DisplayManagerServiceLock).

Bug: 273298889
Bug: 314921822
Test: atest DisplayModeDirectorTest
Test: atest RefreshRateSettingsUtilsTest
Change-Id: I3f7758b4911d7fb296e71489196c1b8a1be61290
parent 9da5e00d
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -33,13 +33,14 @@ public class RefreshRateSettingsUtils {
    /**
     * Find the highest refresh rate among all the modes of the default display.
     *
     * This method will acquire DisplayManager.mLock, so calling it while holding other locks
     * should be done with care.
     * @param context The context
     * @return The highest refresh rate
     */
    public static float findHighestRefreshRateForDefaultDisplay(Context context) {
        final DisplayManager dm = context.getSystemService(DisplayManager.class);
        final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);

        if (display == null) {
            Log.w(TAG, "No valid default display device");
            return DEFAULT_REFRESH_RATE;
+54 −17
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import static android.os.PowerManager.BRIGHTNESS_INVALID_FLOAT;
import static android.view.Display.Mode.INVALID_MODE_ID;

import static com.android.server.display.DisplayDeviceConfig.DEFAULT_LOW_REFRESH_RATE;
import static com.android.internal.display.RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay;

import android.annotation.IntegerRes;
import android.annotation.NonNull;
@@ -232,8 +231,11 @@ public class DisplayModeDirector {
     * is ready.
     */
    public void start(SensorManager sensorManager) {
        mSettingsObserver.observe();
        // This has to be called first to read the supported display modes that will be used by
        // other observers
        mDisplayObserver.observe();

        mSettingsObserver.observe();
        mBrightnessObserver.observe(sensorManager);
        mSensorObserver.observe();
        mHbmObserver.observe();
@@ -608,12 +610,17 @@ public class DisplayModeDirector {
        return mHbmObserver;
    }

    @VisibleForTesting
    DisplayObserver getDisplayObserver() {
        return mDisplayObserver;
    }

    @VisibleForTesting
    DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
            float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
        synchronized (mLock) {
            mSettingsObserver.updateRefreshRateSettingLocked(
                    minRefreshRate, peakRefreshRate, defaultRefreshRate);
            mSettingsObserver.updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate,
                    defaultRefreshRate, Display.DEFAULT_DISPLAY);
            return getDesiredDisplayModeSpecs(Display.DEFAULT_DISPLAY);
        }
    }
@@ -886,19 +893,17 @@ public class DisplayModeDirector {
                if (defaultPeakRefreshRate == null) {
                    setDefaultPeakRefreshRate(mDefaultDisplayDeviceConfig,
                            /* attemptReadFromFeatureParams= */ false);
                    updateRefreshRateSettingLocked();
                } else if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) {
                    mDefaultPeakRefreshRate = defaultPeakRefreshRate;
                    updateRefreshRateSettingLocked();
                }
                updateRefreshRateSettingLocked();
            }
        }

        @Override
        public void onChange(boolean selfChange, Uri uri, int userId) {
            synchronized (mLock) {
                if (mPeakRefreshRateSetting.equals(uri)
                        || mMinRefreshRateSetting.equals(uri)) {
                if (mPeakRefreshRateSetting.equals(uri) || mMinRefreshRateSetting.equals(uri)) {
                    updateRefreshRateSettingLocked();
                } else if (mLowPowerModeSetting.equals(uri)) {
                    updateLowPowerModeSettingLocked();
@@ -958,9 +963,29 @@ public class DisplayModeDirector {
            mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
        }

        /**
         * Update refresh rate settings for all displays
         */
        @GuardedBy("mLock")
        private void updateRefreshRateSettingLocked() {
            for (int i = 0; i < mSupportedModesByDisplay.size(); i++) {
                updateRefreshRateSettingLocked(mSupportedModesByDisplay.keyAt(i));
            }
        }

        /**
         * Update refresh rate settings for a specific display
         * @param displayId The display ID
         */
        @GuardedBy("mLock")
        private void updateRefreshRateSettingLocked(int displayId) {
            final ContentResolver cr = mContext.getContentResolver();
            float highestRefreshRate = findHighestRefreshRateForDefaultDisplay(mContext);
            if (!mSupportedModesByDisplay.contains(displayId)) {
                Slog.e(TAG, "Cannot update refresh rate setting: no supported modes for display "
                        + displayId);
                return;
            }
            float highestRefreshRate = getMaxRefreshRateLocked(displayId);

            float minRefreshRate = Settings.System.getFloatForUser(cr,
                    Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId());
@@ -988,11 +1013,13 @@ public class DisplayModeDirector {
                }
            }

            updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
            updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate,
                    displayId);
        }

        private void updateRefreshRateSettingLocked(
                float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
        @GuardedBy("mLock")
        private void updateRefreshRateSettingLocked(float minRefreshRate, float peakRefreshRate,
                float defaultRefreshRate, int displayId) {
            // TODO(b/156304339): The logic in here, aside from updating the refresh rate votes, is
            // used to predict if we're going to be doing frequent refresh rate switching, and if
            // so, enable the brightness observer. The logic here is more complicated and fragile
@@ -1000,9 +1027,9 @@ public class DisplayModeDirector {
            Vote peakVote = peakRefreshRate == 0f
                    ? null
                    : Vote.forRenderFrameRates(0f, Math.max(minRefreshRate, peakRefreshRate));
            mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
            mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
                    peakVote);
            mVotesStorage.updateGlobalVote(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
            mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
                    Vote.forRenderFrameRates(minRefreshRate, Float.POSITIVE_INFINITY));
            Vote defaultVote =
                    defaultRefreshRate == 0f
@@ -1029,6 +1056,14 @@ public class DisplayModeDirector {
            mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, maxRefreshRate);
        }

        private void removeRefreshRateSetting(int displayId) {
            mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
                    null);
            mVotesStorage.updateVote(displayId, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
                    null);
            mVotesStorage.updateVote(displayId, Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE, null);
        }

        private void updateModeSwitchingTypeSettingLocked() {
            final ContentResolver cr = mContext.getContentResolver();
            int switchingType = Settings.Secure.getIntForUser(
@@ -1159,7 +1194,8 @@ public class DisplayModeDirector {
        }
    }

    private final class DisplayObserver implements DisplayManager.DisplayListener {
    @VisibleForTesting
    public final class DisplayObserver implements DisplayManager.DisplayListener {
        // Note that we can never call into DisplayManager or any of the non-POD classes it
        // returns, while holding mLock since it may call into DMS, which might be simultaneously
        // calling into us already holding its own lock.
@@ -1206,11 +1242,10 @@ public class DisplayModeDirector {
            // Populate existing displays
            SparseArray<Display.Mode[]> modes = new SparseArray<>();
            SparseArray<Display.Mode> defaultModes = new SparseArray<>();
            DisplayInfo info = new DisplayInfo();
            Display[] displays = mInjector.getDisplays();
            for (Display d : displays) {
                final int displayId = d.getDisplayId();
                d.getDisplayInfo(info);
                DisplayInfo info = getDisplayInfo(displayId);
                modes.put(displayId, info.supportedModes);
                defaultModes.put(displayId, info.getDefaultMode());
            }
@@ -1238,6 +1273,7 @@ public class DisplayModeDirector {
            synchronized (mLock) {
                mSupportedModesByDisplay.remove(displayId);
                mDefaultModeByDisplay.remove(displayId);
                mSettingsObserver.removeRefreshRateSetting(displayId);
            }
            updateLayoutLimitedFrameRate(displayId, null);
            removeUserSettingDisplayPreferredSize(displayId);
@@ -1388,6 +1424,7 @@ public class DisplayModeDirector {
                }
                if (changed) {
                    notifyDesiredDisplayModeSpecsChangedLocked();
                    mSettingsObserver.updateRefreshRateSettingLocked(displayId);
                }
            }
        }
+8 −4
Original line number Diff line number Diff line
@@ -72,14 +72,18 @@ public class RefreshRateSettingsUtilsTest {

    @Test
    public void testFindHighestRefreshRateForDefaultDisplay() {
        when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null);
        assertEquals(DEFAULT_REFRESH_RATE,
        when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
        assertEquals(120,
                RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
                /* delta= */ 0);
    }

        when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock);
        assertEquals(120,
    @Test
    public void testFindHighestRefreshRate_DisplayIsNull() {
        when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null);
        assertEquals(DEFAULT_REFRESH_RATE,
                RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext),
                /* delta= */ 0);

    }
}
+130 −24
Original line number Diff line number Diff line
@@ -27,8 +27,6 @@ import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_R
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.dx.mockito.inline.extended.ExtendedMockito.doReturn;

import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertArrayEquals;
@@ -290,6 +288,7 @@ public class DisplayModeDirectorTest {
    };

    private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY;
    private static final int DISPLAY_ID_2 = Display.DEFAULT_DISPLAY + 1;
    private static final int MODE_ID = 1;
    private static final float TRANSITION_POINT = 0.763f;

@@ -1550,23 +1549,39 @@ public class DisplayModeDirectorTest {
    public void testPeakRefreshRate_FlagEnabled() {
        when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
                .thenReturn(true);
        float highestRefreshRate = 130;
        doReturn(highestRefreshRate).when(() ->
                RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
        DisplayModeDirector director =
                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
                new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
        director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);

        Display.Mode[] modes1 = new Display.Mode[] {
                new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 60),
                new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 130),
        };
        Display.Mode[] modes2 = new Display.Mode[] {
                new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 60),
                new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 140),
        };
        SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
        supportedModesByDisplay.put(DISPLAY_ID, modes1);
        supportedModesByDisplay.put(DISPLAY_ID_2, modes2);

        Sensor lightSensor = createLightSensor();
        SensorManager sensorManager = createMockSensorManager(lightSensor);
        director.start(sensorManager);
        director.injectSupportedModesByDisplay(supportedModesByDisplay);

        setPeakRefreshRate(Float.POSITIVE_INFINITY);

        Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
        Vote vote1 = director.getVote(DISPLAY_ID,
                Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
                highestRefreshRate);
        Vote vote2 = director.getVote(DISPLAY_ID_2,
                Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
        assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ 0, /* frameRateHigh= */ 130);
        assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ 0, /* frameRateHigh= */ 140);
    }

    @Test
@@ -1584,32 +1599,85 @@ public class DisplayModeDirectorTest {

        setPeakRefreshRate(peakRefreshRate);

        Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0,
                /* frameRateHigh= */ peakRefreshRate);
    }

    @Test
    public void testPeakRefreshRate_DisplayChanged() {
        when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
                .thenReturn(true);
        DisplayModeDirector director =
                new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
        director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
        mInjector.mDisplayInfo.supportedModes = new Display.Mode[] {
                new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 60),
                new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 130),
        };

        Sensor lightSensor = createLightSensor();
        SensorManager sensorManager = createMockSensorManager(lightSensor);
        director.start(sensorManager);

        setPeakRefreshRate(Float.POSITIVE_INFINITY);

        Vote vote = director.getVote(DISPLAY_ID,
                Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */
                peakRefreshRate);
        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ 130);

        // The highest refresh rate of the display changes
        mInjector.mDisplayInfo.supportedModes = new Display.Mode[] {
                new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 60),
                new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 140),
        };
        director.getDisplayObserver().onDisplayChanged(DISPLAY_ID);

        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE);
        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ 140);
    }

    @Test
    public void testMinRefreshRate_FlagEnabled() {
        when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
                .thenReturn(true);
        float highestRefreshRate = 130;
        doReturn(highestRefreshRate).when(() ->
                RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext));
        DisplayModeDirector director =
                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
                new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
        director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);

        Display.Mode[] modes1 = new Display.Mode[] {
                new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 60),
                new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 130),
        };
        Display.Mode[] modes2 = new Display.Mode[] {
                new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 60),
                new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 140),
        };
        SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
        supportedModesByDisplay.put(DISPLAY_ID, modes1);
        supportedModesByDisplay.put(DISPLAY_ID_2, modes2);

        Sensor lightSensor = createLightSensor();
        SensorManager sensorManager = createMockSensorManager(lightSensor);
        director.start(sensorManager);
        director.injectSupportedModesByDisplay(supportedModesByDisplay);

        setMinRefreshRate(Float.POSITIVE_INFINITY);

        Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
        Vote vote1 = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
        Vote vote2 = director.getVote(DISPLAY_ID_2,
                Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ highestRefreshRate,
        assertVoteForRenderFrameRateRange(vote1, /* frameRateLow= */ 130,
                /* frameRateHigh= */ Float.POSITIVE_INFINITY);
        assertVoteForRenderFrameRateRange(vote2, /* frameRateLow= */ 140,
                /* frameRateHigh= */ Float.POSITIVE_INFINITY);
    }

@@ -1628,12 +1696,49 @@ public class DisplayModeDirectorTest {

        setMinRefreshRate(minRefreshRate);

        Vote vote = director.getVote(Display.DEFAULT_DISPLAY,
                Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ minRefreshRate,
                /* frameRateHigh= */ Float.POSITIVE_INFINITY);
    }

    @Test
    public void testMinRefreshRate_DisplayChanged() {
        when(mDisplayManagerFlags.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled())
                .thenReturn(true);
        DisplayModeDirector director =
                new DisplayModeDirector(mContext, mHandler, mInjector, mDisplayManagerFlags);
        director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
        mInjector.mDisplayInfo.supportedModes = new Display.Mode[] {
                new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 60),
                new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 130),
        };

        Sensor lightSensor = createLightSensor();
        SensorManager sensorManager = createMockSensorManager(lightSensor);
        director.start(sensorManager);

        setMinRefreshRate(Float.POSITIVE_INFINITY);

        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 130,
                /* frameRateHigh= */ Float.POSITIVE_INFINITY);

        // The highest refresh rate of the display changes
        mInjector.mDisplayInfo.supportedModes = new Display.Mode[] {
                new Display.Mode(/* modeId= */ 1, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 60),
                new Display.Mode(/* modeId= */ 2, /* width= */ 1280, /* height= */ 720,
                        /* refreshRate= */ 140),
        };
        director.getDisplayObserver().onDisplayChanged(DISPLAY_ID);

        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE);
        assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 140,
                /* frameRateHigh= */ Float.POSITIVE_INFINITY);
    }

    @Test
    public void testSensorRegistration() {
        // First, configure brightness zones or DMD won't register for sensor data.
@@ -3329,7 +3434,7 @@ public class DisplayModeDirectorTest {
    public static class FakesInjector implements DisplayModeDirector.Injector {
        private final FakeDeviceConfig mDeviceConfig;
        private final DisplayInfo mDisplayInfo;
        private final Display mDisplay;
        private final Map<Integer, Display> mDisplays;
        private boolean mDisplayInfoValid = true;
        private final DisplayManagerInternal mDisplayManagerInternal;
        private final StatusBarManagerInternal mStatusBarManagerInternal;
@@ -3350,7 +3455,8 @@ public class DisplayModeDirectorTest {
            mDisplayInfo.defaultModeId = MODE_ID;
            mDisplayInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID,
                    800, 600, /* refreshRate= */ 60)};
            mDisplay = createDisplay(DISPLAY_ID);
            mDisplays = Map.of(DISPLAY_ID, createDisplay(DISPLAY_ID),
                    DISPLAY_ID_2, createDisplay(DISPLAY_ID_2));
            mDisplayManagerInternal = displayManagerInternal;
            mStatusBarManagerInternal = statusBarManagerInternal;
            mSensorManagerInternal = sensorManagerInternal;
@@ -3381,12 +3487,12 @@ public class DisplayModeDirectorTest {

        @Override
        public Display getDisplay(int displayId) {
            return mDisplay;
            return mDisplays.get(displayId);
        }

        @Override
        public Display[] getDisplays() {
            return new Display[] { mDisplay };
            return mDisplays.values().toArray(new Display[0]);
        }

        @Override