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

Commit 7374be2c authored by Steven Thomas's avatar Steven Thomas Committed by Automerger Merge Worker
Browse files

Merge "Add default frame rate setting" into rvc-dev am: 3d6a1cfb

Change-Id: I68bb3cc49875f96b71463a9109a8a6fa203702a5
parents c13169d8 3d6a1cfb
Loading
Loading
Loading
Loading
+15 −3
Original line number Original line Diff line number Diff line
@@ -4165,9 +4165,21 @@
     and a second time clipped to the fill level to indicate charge -->
     and a second time clipped to the fill level to indicate charge -->
    <bool name="config_batterymeterDualTone">false</bool>
    <bool name="config_batterymeterDualTone">false</bool>


    <!-- The default peak refresh rate for a given device. Change this value if you want to allow
    <!-- The default refresh rate for a given device. Change this value to set a higher default
         for higher refresh rates to be automatically used out of the box -->
         refresh rate. If the hardware composer on the device supports display modes with a higher
    <integer name="config_defaultPeakRefreshRate">60</integer>
         refresh rate than the default value specified here, the framework may use those higher
         refresh rate modes if an app chooses one by setting preferredDisplayModeId or calling
         setFrameRate().
         If a non-zero value is set for config_defaultPeakRefreshRate, then
         config_defaultRefreshRate may be set to 0, in which case the value set for
         config_defaultPeakRefreshRate will act as the default frame rate. -->
    <integer name="config_defaultRefreshRate">60</integer>

    <!-- The default peak refresh rate for a given device. Change this value if you want to prevent
         the framework from using higher refresh rates, even if display modes with higher refresh
         rates are available from hardware composer. Only has an effect if the value is
         non-zero. -->
    <integer name="config_defaultPeakRefreshRate">0</integer>


    <!-- The display uses different gamma curves for different refresh rates. It's hard for panel
    <!-- The display uses different gamma curves for different refresh rates. It's hard for panel
         vendor to tune the curves to have exact same brightness for different refresh rate. So
         vendor to tune the curves to have exact same brightness for different refresh rate. So
+1 −0
Original line number Original line Diff line number Diff line
@@ -3772,6 +3772,7 @@
  <java-symbol type="string" name="bluetooth_airplane_mode_toast" />
  <java-symbol type="string" name="bluetooth_airplane_mode_toast" />


  <!-- For high refresh rate displays -->
  <!-- For high refresh rate displays -->
  <java-symbol type="integer" name="config_defaultRefreshRate" />
  <java-symbol type="integer" name="config_defaultPeakRefreshRate" />
  <java-symbol type="integer" name="config_defaultPeakRefreshRate" />
  <java-symbol type="integer" name="config_defaultRefreshRateInZone" />
  <java-symbol type="integer" name="config_defaultRefreshRateInZone" />
  <java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" />
  <java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" />
+69 −13
Original line number Original line Diff line number Diff line
@@ -92,7 +92,7 @@ public class DisplayModeDirector {
    private final AppRequestObserver mAppRequestObserver;
    private final AppRequestObserver mAppRequestObserver;
    private final SettingsObserver mSettingsObserver;
    private final SettingsObserver mSettingsObserver;
    private final DisplayObserver mDisplayObserver;
    private final DisplayObserver mDisplayObserver;
    private final BrightnessObserver mBrightnessObserver;
    private BrightnessObserver mBrightnessObserver;


    private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
    private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
    private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
    private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
@@ -460,6 +460,21 @@ public class DisplayModeDirector {
        mVotesByDisplay = votesByDisplay;
        mVotesByDisplay = votesByDisplay;
    }
    }


    @VisibleForTesting
    void injectBrightnessObserver(BrightnessObserver brightnessObserver) {
        mBrightnessObserver = brightnessObserver;
    }

    @VisibleForTesting
    DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
            float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
        synchronized (mLock) {
            mSettingsObserver.updateRefreshRateSettingLocked(
                    minRefreshRate, peakRefreshRate, defaultRefreshRate);
            return getDesiredDisplayModeSpecs(Display.DEFAULT_DISPLAY);
        }
    }

    /**
    /**
     * Listens for changes refresh rate coordination.
     * Listens for changes refresh rate coordination.
     */
     */
@@ -666,14 +681,18 @@ public class DisplayModeDirector {


    @VisibleForTesting
    @VisibleForTesting
    static final class Vote {
    static final class Vote {
        // DEFAULT_FRAME_RATE votes for [0, DEFAULT]. As the lowest priority vote, it's overridden
        // by all other considerations. It acts to set a default frame rate for a device.
        public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0;

        // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null.
        // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null.
        // If the higher voters result is a range, it will fix the rate to a single choice.
        // If the higher voters result is a range, it will fix the rate to a single choice.
        // It's used to avoid rate switch in certain conditions.
        // It's used to avoid rate switch in certain conditions.
        public static final int PRIORITY_LOW_BRIGHTNESS = 0;
        public static final int PRIORITY_LOW_BRIGHTNESS = 1;


        // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
        // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
        // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
        // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
        public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 1;
        public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2;


        // We split the app request into different priorities in case we can satisfy one desire
        // We split the app request into different priorities in case we can satisfy one desire
        // without the other.
        // without the other.
@@ -683,20 +702,20 @@ public class DisplayModeDirector {
        // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
        // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
        // System also forces some apps like blacklisted app to run at a lower refresh rate.
        // System also forces some apps like blacklisted app to run at a lower refresh rate.
        // @see android.R.array#config_highRefreshRateBlacklist
        // @see android.R.array#config_highRefreshRateBlacklist
        public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 2;
        public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 3;
        public static final int PRIORITY_APP_REQUEST_SIZE = 3;
        public static final int PRIORITY_APP_REQUEST_SIZE = 4;


        // SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest
        // 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)]
        // of low priority voters. It votes [0, max(PEAK, MIN)]
        public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 4;
        public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 5;


        // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
        // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
        public static final int PRIORITY_LOW_POWER_MODE = 5;
        public static final int PRIORITY_LOW_POWER_MODE = 6;


        // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
        // 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.
        // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.


        public static final int MIN_PRIORITY = PRIORITY_LOW_BRIGHTNESS;
        public static final int MIN_PRIORITY = PRIORITY_DEFAULT_REFRESH_RATE;
        public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE;
        public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE;


        // The cutoff for the app request refresh rate range. Votes with priorities lower than this
        // The cutoff for the app request refresh rate range. Votes with priorities lower than this
@@ -740,6 +759,8 @@ public class DisplayModeDirector {


        public static String priorityToString(int priority) {
        public static String priorityToString(int priority) {
            switch (priority) {
            switch (priority) {
                case PRIORITY_DEFAULT_REFRESH_RATE:
                    return "PRIORITY_DEFAULT_REFRESH_RATE";
                case PRIORITY_LOW_BRIGHTNESS:
                case PRIORITY_LOW_BRIGHTNESS:
                    return "PRIORITY_LOW_BRIGHTNESS";
                    return "PRIORITY_LOW_BRIGHTNESS";
                case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
                case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
@@ -776,12 +797,15 @@ public class DisplayModeDirector {


        private final Context mContext;
        private final Context mContext;
        private float mDefaultPeakRefreshRate;
        private float mDefaultPeakRefreshRate;
        private float mDefaultRefreshRate;


        SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
        SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
            super(handler);
            super(handler);
            mContext = context;
            mContext = context;
            mDefaultPeakRefreshRate = (float) context.getResources().getInteger(
            mDefaultPeakRefreshRate = (float) context.getResources().getInteger(
                    R.integer.config_defaultPeakRefreshRate);
                    R.integer.config_defaultPeakRefreshRate);
            mDefaultRefreshRate =
                    (float) context.getResources().getInteger(R.integer.config_defaultRefreshRate);
        }
        }


        public void observe() {
        public void observe() {
@@ -849,17 +873,48 @@ public class DisplayModeDirector {
                    Settings.System.MIN_REFRESH_RATE, 0f);
                    Settings.System.MIN_REFRESH_RATE, 0f);
            float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
            float peakRefreshRate = Settings.System.getFloat(mContext.getContentResolver(),
                    Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate);
                    Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate);

            updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate);
            updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE,
        }
                    Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate)));

        private void updateRefreshRateSettingLocked(
                float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
            // 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
            // than necessary, and we should improve it. See b/156304339 for more info.
            Vote peakVote = peakRefreshRate == 0f
                    ? null
                    : Vote.forRefreshRates(0f, Math.max(minRefreshRate, peakRefreshRate));
            updateVoteLocked(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, peakVote);
            updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
            updateVoteLocked(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
                    Vote.forRefreshRates(minRefreshRate, Float.POSITIVE_INFINITY));
                    Vote.forRefreshRates(minRefreshRate, Float.POSITIVE_INFINITY));
            Vote defaultVote =
                    defaultRefreshRate == 0f ? null : Vote.forRefreshRates(0f, defaultRefreshRate);
            updateVoteLocked(Vote.PRIORITY_DEFAULT_REFRESH_RATE, defaultVote);

            float maxRefreshRate;
            if (peakRefreshRate == 0f && defaultRefreshRate == 0f) {
                // We require that at least one of the peak or default refresh rate values are
                // set. The brightness observer requires that we're able to predict whether or not
                // we're going to do frequent refresh rate switching, and with the way the code is
                // currently written, we need either a default or peak refresh rate value for that.
                Slog.e(TAG, "Default and peak refresh rates are both 0. One of them should be set"
                        + " to a valid value.");
                maxRefreshRate = minRefreshRate;
            } else if (peakRefreshRate == 0f) {
                maxRefreshRate = defaultRefreshRate;
            } else if (defaultRefreshRate == 0f) {
                maxRefreshRate = peakRefreshRate;
            } else {
                maxRefreshRate = Math.min(defaultRefreshRate, peakRefreshRate);
            }


            mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, peakRefreshRate);
            mBrightnessObserver.onRefreshRateSettingChangedLocked(minRefreshRate, maxRefreshRate);
        }
        }


        public void dumpLocked(PrintWriter pw) {
        public void dumpLocked(PrintWriter pw) {
            pw.println("  SettingsObserver");
            pw.println("  SettingsObserver");
            pw.println("    mDefaultRefreshRate: " + mDefaultRefreshRate);
            pw.println("    mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate);
            pw.println("    mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate);
        }
        }
    }
    }
@@ -1014,7 +1069,8 @@ public class DisplayModeDirector {
     * {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and
     * {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and
     * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
     * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}.
     */
     */
    private class BrightnessObserver extends ContentObserver {
    @VisibleForTesting
    public class BrightnessObserver extends ContentObserver {
        // TODO: brightnessfloat: change this to the float setting
        // TODO: brightnessfloat: change this to the float setting
        private final Uri mDisplayBrightnessSetting =
        private final Uri mDisplayBrightnessSetting =
                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
+81 −14
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.display;
package com.android.server.display;


import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;


import android.content.Context;
import android.content.Context;
import android.os.Handler;
import android.os.Handler;
@@ -28,6 +29,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import androidx.test.runner.AndroidJUnit4;


import com.android.server.display.DisplayModeDirector.BrightnessObserver;
import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs;
import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs;
import com.android.server.display.DisplayModeDirector.Vote;
import com.android.server.display.DisplayModeDirector.Vote;


@@ -36,6 +38,7 @@ import com.google.common.truth.Truth;
import org.junit.Before;
import org.junit.Before;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoAnnotations;


@SmallTest
@SmallTest
@@ -52,16 +55,15 @@ public class DisplayModeDirectorTest {
        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
    }
    }


    private DisplayModeDirector createDisplayModeDirectorWithDisplayFpsRange(
    private DisplayModeDirector createDirectorFromRefreshRateArray(
            int minFps, int maxFps) {
            float[] refreshRates, int baseModeId) {
        DisplayModeDirector director =
        DisplayModeDirector director =
                new DisplayModeDirector(mContext, new Handler(Looper.getMainLooper()));
                new DisplayModeDirector(mContext, new Handler(Looper.getMainLooper()));
        int displayId = 0;
        int displayId = 0;
        int numModes = maxFps - minFps + 1;
        Display.Mode[] modes = new Display.Mode[refreshRates.length];
        Display.Mode[] modes = new Display.Mode[numModes];
        for (int i = 0; i < refreshRates.length; i++) {
        for (int i = minFps; i <= maxFps; i++) {
            modes[i] = new Display.Mode(
            modes[i - minFps] = new Display.Mode(
                    /*modeId=*/baseModeId + i, /*width=*/1000, /*height=*/1000, refreshRates[i]);
                    /*modeId=*/i, /*width=*/1000, /*height=*/1000, /*refreshRate=*/i);
        }
        }
        SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
        SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<>();
        supportedModesByDisplay.put(displayId, modes);
        supportedModesByDisplay.put(displayId, modes);
@@ -72,14 +74,22 @@ public class DisplayModeDirectorTest {
        return director;
        return director;
    }
    }


    private DisplayModeDirector createDirectorFromFpsRange(int minFps, int maxFps) {
        int numRefreshRates = maxFps - minFps + 1;
        float[] refreshRates = new float[numRefreshRates];
        for (int i = 0; i < numRefreshRates; i++) {
            refreshRates[i] = minFps + i;
        }
        return createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/minFps);
    }

    @Test
    @Test
    public void testDisplayModeVoting() {
    public void testDisplayModeVoting() {
        int displayId = 0;
        int displayId = 0;


        // With no votes present, DisplayModeDirector should allow any refresh rate.
        // With no votes present, DisplayModeDirector should allow any refresh rate.
        DesiredDisplayModeSpecs modeSpecs =
        DesiredDisplayModeSpecs modeSpecs =
                createDisplayModeDirectorWithDisplayFpsRange(60, 90).getDesiredDisplayModeSpecs(
                createDirectorFromFpsRange(60, 90).getDesiredDisplayModeSpecs(displayId);
                        displayId);
        Truth.assertThat(modeSpecs.baseModeId).isEqualTo(60);
        Truth.assertThat(modeSpecs.baseModeId).isEqualTo(60);
        Truth.assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(0f);
        Truth.assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(0f);
        Truth.assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(Float.POSITIVE_INFINITY);
        Truth.assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(Float.POSITIVE_INFINITY);
@@ -92,7 +102,7 @@ public class DisplayModeDirectorTest {
        {
        {
            int minFps = 60;
            int minFps = 60;
            int maxFps = 90;
            int maxFps = 90;
            DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
            DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
            assertTrue(2 * numPriorities < maxFps - minFps + 1);
            assertTrue(2 * numPriorities < maxFps - minFps + 1);
            SparseArray<Vote> votes = new SparseArray<>();
            SparseArray<Vote> votes = new SparseArray<>();
            SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
            SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -114,7 +124,7 @@ public class DisplayModeDirectorTest {
        // presence of higher priority votes.
        // presence of higher priority votes.
        {
        {
            assertTrue(numPriorities >= 2);
            assertTrue(numPriorities >= 2);
            DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
            DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
            SparseArray<Vote> votes = new SparseArray<>();
            SparseArray<Vote> votes = new SparseArray<>();
            SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
            SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
            votesByDisplay.put(displayId, votes);
            votesByDisplay.put(displayId, votes);
@@ -131,7 +141,7 @@ public class DisplayModeDirectorTest {
    @Test
    @Test
    public void testVotingWithFloatingPointErrors() {
    public void testVotingWithFloatingPointErrors() {
        int displayId = 0;
        int displayId = 0;
        DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(50, 90);
        DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
        SparseArray<Vote> votes = new SparseArray<>();
        SparseArray<Vote> votes = new SparseArray<>();
        SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
        SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
        votesByDisplay.put(displayId, votes);
        votesByDisplay.put(displayId, votes);
@@ -154,7 +164,7 @@ public class DisplayModeDirectorTest {
        assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE);
        assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE);


        int displayId = 0;
        int displayId = 0;
        DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
        DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
        SparseArray<Vote> votes = new SparseArray<>();
        SparseArray<Vote> votes = new SparseArray<>();
        SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
        SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
        votesByDisplay.put(displayId, votes);
        votesByDisplay.put(displayId, votes);
@@ -202,7 +212,7 @@ public class DisplayModeDirectorTest {
                >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
                >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);


        int displayId = 0;
        int displayId = 0;
        DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
        DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
        SparseArray<Vote> votes = new SparseArray<>();
        SparseArray<Vote> votes = new SparseArray<>();
        SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
        SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
        votesByDisplay.put(displayId, votes);
        votesByDisplay.put(displayId, votes);
@@ -235,4 +245,61 @@ public class DisplayModeDirectorTest {
                .isWithin(FLOAT_TOLERANCE)
                .isWithin(FLOAT_TOLERANCE)
                .of(75);
                .of(75);
    }
    }

    void verifySpecsWithRefreshRateSettings(DisplayModeDirector director, float minFps,
            float peakFps, float defaultFps, float primaryMin, float primaryMax,
            float appRequestMin, float appRequestMax) {
        DesiredDisplayModeSpecs specs = director.getDesiredDisplayModeSpecsWithInjectedFpsSettings(
                minFps, peakFps, defaultFps);
        Truth.assertThat(specs.primaryRefreshRateRange.min).isEqualTo(primaryMin);
        Truth.assertThat(specs.primaryRefreshRateRange.max).isEqualTo(primaryMax);
        Truth.assertThat(specs.appRequestRefreshRateRange.min).isEqualTo(appRequestMin);
        Truth.assertThat(specs.appRequestRefreshRateRange.max).isEqualTo(appRequestMax);
    }

    @Test
    public void testSpecsFromRefreshRateSettings() {
        // Confirm that, with varying settings for min, peak, and default refresh rate,
        // DesiredDisplayModeSpecs is calculated correctly.
        float[] refreshRates = {30.f, 60.f, 90.f, 120.f, 150.f};
        DisplayModeDirector director =
                createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/0);

        float inf = Float.POSITIVE_INFINITY;
        verifySpecsWithRefreshRateSettings(director, 0, 0, 0, 0, inf, 0, inf);
        verifySpecsWithRefreshRateSettings(director, 0, 0, 90, 0, 90, 0, inf);
        verifySpecsWithRefreshRateSettings(director, 0, 90, 0, 0, 90, 0, 90);
        verifySpecsWithRefreshRateSettings(director, 0, 90, 60, 0, 60, 0, 90);
        verifySpecsWithRefreshRateSettings(director, 0, 90, 120, 0, 90, 0, 90);
        verifySpecsWithRefreshRateSettings(director, 90, 0, 0, 90, inf, 0, inf);
        verifySpecsWithRefreshRateSettings(director, 90, 0, 120, 90, 120, 0, inf);
        verifySpecsWithRefreshRateSettings(director, 90, 0, 60, 90, inf, 0, inf);
        verifySpecsWithRefreshRateSettings(director, 90, 120, 0, 90, 120, 0, 120);
        verifySpecsWithRefreshRateSettings(director, 90, 60, 0, 90, 90, 0, 90);
        verifySpecsWithRefreshRateSettings(director, 60, 120, 90, 60, 90, 0, 120);
    }

    void verifyBrightnessObserverCall(DisplayModeDirector director, float minFps, float peakFps,
            float defaultFps, float brightnessObserverMin, float brightnessObserverMax) {
        BrightnessObserver brightnessObserver = Mockito.mock(BrightnessObserver.class);
        director.injectBrightnessObserver(brightnessObserver);
        director.getDesiredDisplayModeSpecsWithInjectedFpsSettings(minFps, peakFps, defaultFps);
        verify(brightnessObserver)
                .onRefreshRateSettingChangedLocked(brightnessObserverMin, brightnessObserverMax);
    }

    @Test
    public void testBrightnessObserverCallWithRefreshRateSettings() {
        // Confirm that, with varying settings for min, peak, and default refresh rate, we make the
        // correct call to the brightness observer.
        float[] refreshRates = {60.f, 90.f, 120.f};
        DisplayModeDirector director =
                createDirectorFromRefreshRateArray(refreshRates, /*baseModeId=*/0);
        verifyBrightnessObserverCall(director, 0, 0, 0, 0, 0);
        verifyBrightnessObserverCall(director, 0, 0, 90, 0, 90);
        verifyBrightnessObserverCall(director, 0, 90, 0, 0, 90);
        verifyBrightnessObserverCall(director, 0, 90, 60, 0, 60);
        verifyBrightnessObserverCall(director, 90, 90, 0, 90, 90);
        verifyBrightnessObserverCall(director, 120, 90, 0, 120, 90);
    }
}
}