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

Commit 4e844a6d authored by Piotr Wilczyński's avatar Piotr Wilczyński Committed by Android (Google) Code Review
Browse files

Merge "Min and max refresh rate per display (take 2)" into main

parents 0bf0823b 9fc75134
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;
@@ -238,8 +237,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();
@@ -619,12 +621,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);
        }
    }
@@ -897,19 +904,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();
@@ -969,9 +974,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());
@@ -1009,11 +1034,13 @@ public class DisplayModeDirector {
                        Float.POSITIVE_INFINITY, cr.getUserId());
            }

            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
@@ -1021,9 +1048,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
@@ -1050,6 +1077,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(
@@ -1180,7 +1215,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.
@@ -1227,11 +1263,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());
            }
@@ -1259,6 +1294,7 @@ public class DisplayModeDirector {
            synchronized (mLock) {
                mSupportedModesByDisplay.remove(displayId);
                mDefaultModeByDisplay.remove(displayId);
                mSettingsObserver.removeRefreshRateSetting(displayId);
            }
            updateLayoutLimitedFrameRate(displayId, null);
            removeUserSettingDisplayPreferredSize(displayId);
@@ -1409,6 +1445,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