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

Commit 26c83452 authored by Ady Abraham's avatar Ady Abraham
Browse files

Add preferredMaxDisplayRefreshRate

Add a new private window attribute for allowing apps to specify the max
display refresh rate. This is useful for use cases such as keyguard
where the refresh rate should be limited to preserve power.

In the next CLs the preferredDiplayModeId would be enabled for
frame rate override (60-on-120) and the preferredMaxDisplayRefreshRate
would be the alternative to control the display refresh rate (as opposed
to the frame rate experienced by the app).

Test: atest RefreshRatePolicyTest
Test: atest DisplayModeDirectorTest
Test: Launch camera app and observe refresh rate
Bug: 183226498
Change-Id: I1c5e9f6047cbea4bfb581251b8dd2b9058b3e378
parent a91e5123
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -190,6 +190,8 @@ public abstract class DisplayManagerInternal {
     * has a preference.
     * @param requestedModeId The preferred mode id for the top-most visible window that has a
     * preference.
     * @param requestedMaxRefreshRate The preferred highest refresh rate for the top-most visible
     *                                window that has a preference.
     * @param requestedMinimalPostProcessing The preferred minimal post processing setting for the
     * display. This is true when there is at least one visible window that wants minimal post
     * processng on.
@@ -197,8 +199,8 @@ public abstract class DisplayManagerInternal {
     * prior to call to performTraversalInTransactionFromWindowManager.
     */
    public abstract void setDisplayProperties(int displayId, boolean hasContent,
            float requestedRefreshRate, int requestedModeId, boolean requestedMinimalPostProcessing,
            boolean inTraversal);
            float requestedRefreshRate, int requestedModeId, float requestedMaxRefreshRate,
            boolean requestedMinimalPostProcessing, boolean inTraversal);

    /**
     * Applies an offset to the contents of a display, for example to avoid burn-in.
+8 −0
Original line number Diff line number Diff line
@@ -3009,6 +3009,14 @@ public interface WindowManager extends ViewManager {
         */
        public int preferredDisplayModeId;

        /**
         * The max display refresh rate while the window is in focus.
         *
         * This value is ignored if {@link #preferredDisplayModeId} is set.
         * @hide
         */
        public float preferredMaxDisplayRefreshRate;

        /**
         * An internal annotation for flags that can be specified to {@link #systemUiVisibility}
         * and {@link #subtreeSystemUiVisibility}.
+8 −14
Original line number Diff line number Diff line
@@ -61,7 +61,6 @@ import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
@@ -85,7 +84,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
    private final LayoutParams mLpChanged;
    private final boolean mKeyguardScreenRotation;
    private final long mLockScreenDisplayTimeout;
    private final Display.Mode mKeyguardDisplayMode;
    private final float mKeyguardRefreshRate;
    private final KeyguardViewMediator mKeyguardViewMediator;
    private final KeyguardBypassController mKeyguardBypassController;
    private ViewGroup mNotificationShadeView;
@@ -135,14 +134,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
        // Running on the highest frame rate available can be expensive.
        // Let's specify a preferred refresh rate, and allow higher FPS only when we
        // know that we're not falsing (because we unlocked.)
        int keyguardRefreshRate = context.getResources()
        mKeyguardRefreshRate = context.getResources()
                .getInteger(R.integer.config_keyguardRefreshRate);
        // Find supported display mode with the same resolution and requested refresh rate.
        mKeyguardDisplayMode = Arrays.stream(supportedModes).filter(mode ->
                (int) mode.getRefreshRate() == keyguardRefreshRate
                        && mode.getPhysicalWidth() == currentMode.getPhysicalWidth()
                        && mode.getPhysicalHeight() == currentMode.getPhysicalHeight())
                .findFirst().orElse(null);
    }

    /**
@@ -273,16 +266,17 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
            mLpChanged.privateFlags &= ~LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
        }

        if (mKeyguardDisplayMode != null) {
        if (mKeyguardRefreshRate > 0) {
            boolean bypassOnKeyguard = mKeyguardBypassController.getBypassEnabled()
                    && state.mStatusBarState == StatusBarState.KEYGUARD
                    && !state.mKeyguardFadingAway && !state.mKeyguardGoingAway;
            if (state.mDozing || bypassOnKeyguard) {
                mLpChanged.preferredDisplayModeId = mKeyguardDisplayMode.getModeId();
                mLpChanged.preferredMaxDisplayRefreshRate = mKeyguardRefreshRate;
            } else {
                mLpChanged.preferredDisplayModeId = 0;
                mLpChanged.preferredMaxDisplayRefreshRate = 0;
            }
            Trace.setCounter("display_mode_id", mLpChanged.preferredDisplayModeId);
            Trace.setCounter("display_max_refresh_rate",
                    (long) mLpChanged.preferredMaxDisplayRefreshRate);
        }
    }

@@ -669,7 +663,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
    @Override
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        pw.println(TAG + ":");
        pw.println("  mKeyguardDisplayMode=" + mKeyguardDisplayMode);
        pw.println("  mKeyguardRefreshRate=" + mKeyguardRefreshRate);
        pw.println(mCurrentState);
        if (mNotificationShadeView != null && mNotificationShadeView.getViewRootImpl() != null) {
            mNotificationShadeView.getViewRootImpl().dump("  ", pw);
+7 −6
Original line number Diff line number Diff line
@@ -1498,8 +1498,8 @@ public final class DisplayManagerService extends SystemService {
    }

    private void setDisplayPropertiesInternal(int displayId, boolean hasContent,
            float requestedRefreshRate, int requestedModeId, boolean preferMinimalPostProcessing,
            boolean inTraversal) {
            float requestedRefreshRate, int requestedModeId, float requestedMaxRefreshRate,
            boolean preferMinimalPostProcessing, boolean inTraversal) {
        synchronized (mSyncRoot) {
            final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
            if (display == null) {
@@ -1523,8 +1523,8 @@ public final class DisplayManagerService extends SystemService {
                requestedModeId = display.getDisplayInfoLocked().findDefaultModeByRefreshRate(
                        requestedRefreshRate).getModeId();
            }
            mDisplayModeDirector.getAppRequestObserver().setAppRequestedMode(
                    displayId, requestedModeId);
            mDisplayModeDirector.getAppRequestObserver().setAppRequest(
                    displayId, requestedModeId, requestedMaxRefreshRate);

            if (display.getDisplayInfoLocked().minimalPostProcessingSupported) {
                boolean mppRequest = mMinimalPostProcessingAllowed && preferMinimalPostProcessing;
@@ -3189,10 +3189,11 @@ public final class DisplayManagerService extends SystemService {

        @Override
        public void setDisplayProperties(int displayId, boolean hasContent,
                float requestedRefreshRate, int requestedMode,
                float requestedRefreshRate, int requestedMode, float requestedMaxRefreshRate,
                boolean requestedMinimalPostProcessing, boolean inTraversal) {
            setDisplayPropertiesInternal(displayId, hasContent, requestedRefreshRate,
                    requestedMode, requestedMinimalPostProcessing, inTraversal);
                    requestedMode, requestedMaxRefreshRate, requestedMinimalPostProcessing,
                    inTraversal);
        }

        @Override
+46 −7
Original line number Diff line number Diff line
@@ -922,6 +922,11 @@ public class DisplayModeDirector {
        // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
        public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2;

        // APP_REQUEST_MAX_REFRESH_RATE is used to for internal apps to limit the refresh
        // rate in certain cases, mostly to preserve power.
        // It votes to [0, APP_REQUEST_MAX_REFRESH_RATE].
        public static final int PRIORITY_APP_REQUEST_MAX_REFRESH_RATE = 3;

        // We split the app request into different priorities in case we can satisfy one desire
        // without the other.

@@ -930,19 +935,19 @@ public class DisplayModeDirector {
        // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
        // System also forces some apps like denylisted app to run at a lower refresh rate.
        // @see android.R.array#config_highRefreshRateBlacklist
        public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 3;
        public static final int PRIORITY_APP_REQUEST_SIZE = 4;
        public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 4;
        public static final int PRIORITY_APP_REQUEST_SIZE = 5;

        // 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)]
        public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 5;
        public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 6;

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

        // The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
        // to function, so this needs to be the highest priority of all votes.
        public static final int PRIORITY_UDFPS = 7;
        public static final int PRIORITY_UDFPS = 8;

        // 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.
@@ -953,7 +958,7 @@ public class DisplayModeDirector {
        // The cutoff for the app request refresh rate range. Votes with priorities lower than this
        // value will not be considered when constructing the app request refresh rate range.
        public static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
                PRIORITY_APP_REQUEST_REFRESH_RATE;
                PRIORITY_APP_REQUEST_MAX_REFRESH_RATE;

        /**
         * A value signifying an invalid width or height in a vote.
@@ -997,6 +1002,8 @@ public class DisplayModeDirector {
                    return "PRIORITY_FLICKER";
                case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
                    return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
                case PRIORITY_APP_REQUEST_MAX_REFRESH_RATE:
                    return "PRIORITY_APP_REQUEST_MAX_REFRESH_RATE";
                case PRIORITY_APP_REQUEST_REFRESH_RATE:
                    return "PRIORITY_APP_REQUEST_REFRESH_RATE";
                case PRIORITY_APP_REQUEST_SIZE:
@@ -1182,14 +1189,17 @@ public class DisplayModeDirector {

    final class AppRequestObserver {
        private final SparseArray<Display.Mode> mAppRequestedModeByDisplay;
        private final SparseArray<Float> mAppPreferredMaxRefreshRateByDisplay;

        AppRequestObserver() {
            mAppRequestedModeByDisplay = new SparseArray<>();
            mAppPreferredMaxRefreshRateByDisplay = new SparseArray<>();
        }

        public void setAppRequestedMode(int displayId, int modeId) {
        public void setAppRequest(int displayId, int modeId, float requestedMaxRefreshRate) {
            synchronized (mLock) {
                setAppRequestedModeLocked(displayId, modeId);
                setAppPreferredMaxRefreshRateLocked(displayId, requestedMaxRefreshRate);
            }
        }

@@ -1217,6 +1227,29 @@ public class DisplayModeDirector {
            updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
        }

        private void setAppPreferredMaxRefreshRateLocked(int displayId,
                float requestedMaxRefreshRate) {
            final Vote vote;
            final Float requestedMaxRefreshRateVote =
                    requestedMaxRefreshRate > 0
                            ? new Float(requestedMaxRefreshRate) : null;
            if (Objects.equals(requestedMaxRefreshRateVote,
                    mAppPreferredMaxRefreshRateByDisplay.get(displayId))) {
                return;
            }

            if (requestedMaxRefreshRate > 0) {
                mAppPreferredMaxRefreshRateByDisplay.put(displayId, requestedMaxRefreshRateVote);
                vote = Vote.forRefreshRates(0, requestedMaxRefreshRate);
            } else {
                mAppPreferredMaxRefreshRateByDisplay.remove(displayId);
                vote = null;
            }
            synchronized (mLock) {
                updateVoteLocked(displayId, Vote.PRIORITY_APP_REQUEST_MAX_REFRESH_RATE, vote);
            }
        }

        private Display.Mode findModeByIdLocked(int displayId, int modeId) {
            Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
            if (modes == null) {
@@ -1238,6 +1271,12 @@ public class DisplayModeDirector {
                final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i);
                pw.println("    " + id + " -> " + mode);
            }
            pw.println("    mAppPreferredMaxRefreshRateByDisplay:");
            for (int i = 0; i < mAppPreferredMaxRefreshRateByDisplay.size(); i++) {
                final int id = mAppPreferredMaxRefreshRateByDisplay.keyAt(i);
                final Float refreshRate = mAppPreferredMaxRefreshRateByDisplay.valueAt(i);
                pw.println("    " + id + " -> " + refreshRate);
            }
        }
    }

Loading