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

Commit 93c953dc authored by petsjonkin's avatar petsjonkin Committed by Oleg Petšjonkin
Browse files

DisplayManager to ignore preferredRefreshRate app request

The request is handled by SF directly and does not require DM to vote for mode change

Bug: b/330810426
Test: atest AppRequestObserverTest
Change-Id: Icb8e9aef5a1e26fcb8c95a6ab93371361a68dd4c
parent 731423a0
Loading
Loading
Loading
Loading
+2 −14
Original line number Diff line number Diff line
@@ -2764,21 +2764,9 @@ public final class DisplayManagerService extends SystemService {
                display.setHasContentLocked(hasContent);
                shouldScheduleTraversal = true;
            }
            if (requestedModeId == 0 && requestedRefreshRate != 0) {
                // Scan supported modes returned by display.getInfo() to find a mode with the same
                // size as the default display mode but with the specified refresh rate instead.
                Display.Mode mode = display.getDisplayInfoLocked().findDefaultModeByRefreshRate(
                        requestedRefreshRate);
                if (mode != null) {
                    requestedModeId = mode.getModeId();
                } else {
                    Slog.e(TAG, "Couldn't find a mode for the requestedRefreshRate: "
                            + requestedRefreshRate + " on Display: " + displayId);
                }
            }

            mDisplayModeDirector.getAppRequestObserver().setAppRequest(
                    displayId, requestedModeId, requestedMinRefreshRate, requestedMaxRefreshRate);
            mDisplayModeDirector.getAppRequestObserver().setAppRequest(displayId, requestedModeId,
                    requestedRefreshRate, requestedMinRefreshRate, requestedMaxRefreshRate);

            // TODO(b/202378408) set minimal post-processing only if it's supported once we have a
            // separate API for disabling on-device processing.
+12 −0
Original line number Diff line number Diff line
@@ -159,6 +159,11 @@ public class DisplayManagerFlags {
            Flags::enablePeakRefreshRatePhysicalLimit
    );

    private final FlagState mIgnoreAppPreferredRefreshRate = new FlagState(
            Flags.FLAG_IGNORE_APP_PREFERRED_REFRESH_RATE_REQUEST,
            Flags::ignoreAppPreferredRefreshRateRequest
    );

    /**
     * @return {@code true} if 'port' is allowed in display layout configuration file.
     */
@@ -321,6 +326,13 @@ public class DisplayManagerFlags {
        return mPeakRefreshRatePhysicalLimit.isEnabled();
    }

    /**
     * @return Whether to ignore preferredRefreshRate app request or not
     */
    public boolean ignoreAppPreferredRefreshRateRequest() {
        return mIgnoreAppPreferredRefreshRate.isEnabled();
    }

    /**
     * dumps all flagstates
     * @param pw printWriter
+11 −0
Original line number Diff line number Diff line
@@ -255,3 +255,14 @@ flag {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "ignore_app_preferred_refresh_rate_request"
    namespace: "display_manager"
    description: "Feature flag for DisplayManager to ignore preferred refresh rate app request. It will be handled by SF only."
    bug: "330810426"
    is_fixed_read_only: true
    metadata {
      purpose: PURPOSE_BUGFIX
    }
}
+35 −3
Original line number Diff line number Diff line
@@ -222,7 +222,7 @@ public class DisplayModeDirector {
                displayManagerFlags.isRefreshRateVotingTelemetryEnabled());
        mSupportedModesByDisplay = new SparseArray<>();
        mDefaultModeByDisplay = new SparseArray<>();
        mAppRequestObserver = new AppRequestObserver();
        mAppRequestObserver = new AppRequestObserver(displayManagerFlags);
        mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig());
        mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
        mSettingsObserver = new SettingsObserver(context, handler, displayManagerFlags);
@@ -1205,17 +1205,32 @@ public class DisplayModeDirector {
    public final class AppRequestObserver {
        private final SparseArray<Display.Mode> mAppRequestedModeByDisplay;
        private final SparseArray<RefreshRateRange> mAppPreferredRefreshRateRangeByDisplay;
        private final boolean mIgnorePreferredRefreshRate;

        AppRequestObserver() {
        AppRequestObserver(DisplayManagerFlags flags) {
            mAppRequestedModeByDisplay = new SparseArray<>();
            mAppPreferredRefreshRateRangeByDisplay = new SparseArray<>();
            mIgnorePreferredRefreshRate = flags.ignoreAppPreferredRefreshRateRequest();
        }

        /**
         * Sets refresh rates from app request
         */
        public void setAppRequest(int displayId, int modeId,
        public void setAppRequest(int displayId, int modeId, float requestedRefreshRate,
                float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) {

            if (modeId == 0 && requestedRefreshRate != 0 && !mIgnorePreferredRefreshRate) {
                // Scan supported modes returned to find a mode with the same
                // size as the default display mode but with the specified refresh rate instead.
                Display.Mode mode = findDefaultModeByRefreshRate(displayId, requestedRefreshRate);
                if (mode != null) {
                    modeId = mode.getModeId();
                } else {
                    Slog.e(TAG, "Couldn't find a mode for the requestedRefreshRate: "
                            + requestedRefreshRate + " on Display: " + displayId);
                }
            }

            synchronized (mLock) {
                setAppRequestedModeLocked(displayId, modeId);
                setAppPreferredRefreshRateRangeLocked(displayId, requestedMinRefreshRateRange,
@@ -1223,6 +1238,23 @@ public class DisplayModeDirector {
            }
        }

        @Nullable
        private Display.Mode findDefaultModeByRefreshRate(int displayId, float refreshRate) {
            Display.Mode[] modes;
            Display.Mode defaultMode;
            synchronized (mLock) {
                modes = mSupportedModesByDisplay.get(displayId);
                defaultMode = mDefaultModeByDisplay.get(displayId);
            }
            for (int i = 0; i < modes.length; i++) {
                if (modes[i].matches(defaultMode.getPhysicalWidth(),
                        defaultMode.getPhysicalHeight(), refreshRate)) {
                    return modes[i];
                }
            }
            return null;
        }

        private void setAppRequestedModeLocked(int displayId, int modeId) {
            final Display.Mode requestedMode = findModeByIdLocked(displayId, modeId);
            if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
+122 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.display.mode

import android.content.Context
import android.util.SparseArray
import android.view.Display
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.SmallTest
import com.android.server.display.feature.DisplayManagerFlags
import com.android.server.display.mode.DisplayModeDirector.DisplayDeviceConfigProvider
import com.android.server.display.mode.RefreshRateVote.RenderVote
import com.android.server.testutils.TestHandler
import com.google.common.truth.Truth.assertThat
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

@SmallTest
@RunWith(TestParameterInjector::class)
class AppRequestObserverTest {

    private lateinit var context: Context
    private val mockInjector = mock<DisplayModeDirector.Injector>()
    private val mockFlags = mock<DisplayManagerFlags>()
    private val mockDisplayDeviceConfigProvider = mock<DisplayDeviceConfigProvider>()
    private val testHandler = TestHandler(null)

    @Before
    fun setUp() {
        context = ApplicationProvider.getApplicationContext()
    }

    @Test
    fun `test app request votes`(@TestParameter testCase: AppRequestTestCase) {
        whenever(mockFlags.ignoreAppPreferredRefreshRateRequest())
                .thenReturn(testCase.ignoreRefreshRateRequest)
        val displayModeDirector = DisplayModeDirector(
            context, testHandler, mockInjector, mockFlags, mockDisplayDeviceConfigProvider)
        val modes = arrayOf(
            Display.Mode(1, 1000, 1000, 60f),
            Display.Mode(2, 1000, 1000, 90f),
            Display.Mode(3, 1000, 1000, 120f)
        )
        displayModeDirector.injectSupportedModesByDisplay(SparseArray<Array<Display.Mode>>().apply {
            append(Display.DEFAULT_DISPLAY, modes)
        })
        displayModeDirector.injectDefaultModeByDisplay(SparseArray<Display.Mode>().apply {
            append(Display.DEFAULT_DISPLAY, modes[0])
        })

        displayModeDirector.appRequestObserver.setAppRequest(Display.DEFAULT_DISPLAY,
            testCase.modeId,
            testCase.requestedRefreshRates,
            testCase.requestedMinRefreshRates,
            testCase.requestedMaxRefreshRates)

        val baseModeVote = displayModeDirector.getVote(Display.DEFAULT_DISPLAY,
            Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE)
        assertThat(baseModeVote).isEqualTo(testCase.expectedBaseModeVote)

        val sizeVote = displayModeDirector.getVote(Display.DEFAULT_DISPLAY,
            Vote.PRIORITY_APP_REQUEST_SIZE)
        assertThat(sizeVote).isEqualTo(testCase.expectedSizeVote)

        val renderRateVote = displayModeDirector.getVote(Display.DEFAULT_DISPLAY,
                Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE)
        assertThat(renderRateVote).isEqualTo(testCase.expectedRenderRateVote)
    }

    enum class AppRequestTestCase(
        val ignoreRefreshRateRequest: Boolean,
        val modeId: Int,
        val requestedRefreshRates: Float,
        val requestedMinRefreshRates: Float,
        val requestedMaxRefreshRates: Float,
        internal val expectedBaseModeVote: Vote?,
        internal val expectedSizeVote: Vote?,
        internal val expectedRenderRateVote: Vote?,
    ) {
        BASE_MODE_60(true, 1, 0f, 0f, 0f,
            BaseModeRefreshRateVote(60f), SizeVote(1000, 1000, 1000, 1000), null),
        BASE_MODE_90(true, 2, 0f, 0f, 0f,
            BaseModeRefreshRateVote(90f), SizeVote(1000, 1000, 1000, 1000), null),
        MIN_REFRESH_RATE_60(true, 0, 0f, 60f, 0f,
            null, null, RenderVote(60f, Float.POSITIVE_INFINITY)),
        MIN_REFRESH_RATE_120(true, 0, 0f, 120f, 0f,
        null, null, RenderVote(120f, Float.POSITIVE_INFINITY)),
        MAX_REFRESH_RATE_60(true, 0, 0f, 0f, 60f,
            null, null, RenderVote(0f, 60f)),
        MAX_REFRESH_RATE_120(true, 0, 0f, 0f, 120f,
            null, null, RenderVote(0f, 120f)),
        INVALID_MIN_MAX_REFRESH_RATE(true, 0, 0f, 90f, 60f,
        null, null, null),
        BASE_MODE_MIN_MAX(true, 1, 0f, 60f, 90f,
            BaseModeRefreshRateVote(60f), SizeVote(1000, 1000, 1000, 1000), RenderVote(60f, 90f)),
        PREFERRED_REFRESH_RATE(false, 0, 60f, 0f, 0f,
            BaseModeRefreshRateVote(60f), SizeVote(1000, 1000, 1000, 1000), null),
        PREFERRED_REFRESH_RATE_IGNORED(true, 0, 60f, 0f, 0f,
            null, null, null),
        PREFERRED_REFRESH_RATE_INVALID(false, 0, 45f, 0f, 0f,
            null, null, null),
    }
}
 No newline at end of file
Loading