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

Commit 3ad44da5 authored by Oleg Petšjonkin's avatar Oleg Petšjonkin Committed by Automerger Merge Worker
Browse files

Merge "Extracting VotesStorage to separate class with local locking" into udc-dev am: 24dcc93d

parents d6817b1d 24dcc93d
Loading
Loading
Loading
Loading
+41 −356

File changed.

Preview size limit exceeded, changes collapsed.

+13 −13
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
        DisplayManager.DisplayListener {
    private static final String TAG = "SkinThermalStatusObserver";

    private final DisplayModeDirector.BallotBox mBallotBox;
    private final VotesStorage mVotesStorage;
    private final DisplayModeDirector.Injector mInjector;

    private boolean mLoggingEnabled;
@@ -52,15 +52,15 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
            mThermalThrottlingByDisplay = new SparseArray<>();

    SkinThermalStatusObserver(DisplayModeDirector.Injector injector,
            DisplayModeDirector.BallotBox ballotBox) {
        this(injector, ballotBox, BackgroundThread.getHandler());
            VotesStorage votesStorage) {
        this(injector, votesStorage, BackgroundThread.getHandler());
    }

    @VisibleForTesting
    SkinThermalStatusObserver(DisplayModeDirector.Injector injector,
            DisplayModeDirector.BallotBox ballotBox, Handler handler) {
            VotesStorage votesStorage, Handler handler) {
        mInjector = injector;
        mBallotBox = ballotBox;
        mVotesStorage = votesStorage;
        mHandler = handler;
    }

@@ -112,8 +112,8 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
    public void onDisplayRemoved(int displayId) {
        synchronized (mThermalObserverLock) {
            mThermalThrottlingByDisplay.remove(displayId);
            mHandler.post(() -> mBallotBox.vote(displayId,
                    DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE, null));
            mHandler.post(() -> mVotesStorage.updateVote(displayId,
                    Vote.PRIORITY_SKIN_TEMPERATURE, null));
        }
        if (mLoggingEnabled) {
            Slog.d(TAG, "Display removed and voted: displayId=" + displayId);
@@ -218,11 +218,11 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme
        SurfaceControl.RefreshRateRange foundRange = findBestMatchingRefreshRateRange(currentStatus,
                throttlingMap);
        // if status <= currentStatus not found in the map reset vote
        DisplayModeDirector.Vote vote = null;
        Vote vote = null;
        if (foundRange != null) { // otherwise vote with found range
            vote = DisplayModeDirector.Vote.forRenderFrameRates(foundRange.min, foundRange.max);
            vote = Vote.forRenderFrameRates(foundRange.min, foundRange.max);
        }
        mBallotBox.vote(displayId, DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE, vote);
        mVotesStorage.updateVote(displayId, Vote.PRIORITY_SKIN_TEMPERATURE, vote);
        if (mLoggingEnabled) {
            Slog.d(TAG, "Voted: vote=" + vote + ", display =" + displayId);
        }
@@ -244,11 +244,11 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme

    private void fallbackReportThrottlingIfNeeded(int displayId,
            @Temperature.ThrottlingStatus int currentStatus) {
        DisplayModeDirector.Vote vote = null;
        Vote vote = null;
        if (currentStatus >= Temperature.THROTTLING_CRITICAL) {
            vote = DisplayModeDirector.Vote.forRenderFrameRates(0f, 60f);
            vote = Vote.forRenderFrameRates(0f, 60f);
        }
        mBallotBox.vote(displayId, DisplayModeDirector.Vote.PRIORITY_SKIN_TEMPERATURE, vote);
        mVotesStorage.updateVote(displayId, Vote.PRIORITY_SKIN_TEMPERATURE, vote);
        if (mLoggingEnabled) {
            Slog.d(TAG, "Voted(fallback): vote=" + vote + ", display =" + displayId);
        }
+237 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.view.SurfaceControl;

final class Vote {
    // DEFAULT_RENDER_FRAME_RATE votes for render frame rate [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.
    static final int PRIORITY_DEFAULT_RENDER_FRAME_RATE = 0;

    // PRIORITY_FLICKER_REFRESH_RATE votes for a single refresh rate like [60,60], [90,90] or
    // null. It is used to set a preferred refresh rate value in case the higher priority votes
    // result is a range.
    static final int PRIORITY_FLICKER_REFRESH_RATE = 1;

    // High-brightness-mode may need a specific range of refresh-rates to function properly.
    static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 2;

    // SETTING_MIN_RENDER_FRAME_RATE is used to propose a lower bound of the render frame rate.
    // It votes [minRefreshRate, Float.POSITIVE_INFINITY]
    static final int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3;

    // APP_REQUEST_RENDER_FRAME_RATE_RANGE is used to for internal apps to limit the render
    // frame rate in certain cases, mostly to preserve power.
    // @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate
    // @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate
    // It votes to [preferredMinRefreshRate, preferredMaxRefreshRate].
    static final int PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE = 4;

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

    // Application can specify preferred refresh rate with below attrs.
    // @see android.view.WindowManager.LayoutParams#preferredRefreshRate
    // @see android.view.WindowManager.LayoutParams#preferredDisplayModeId
    //
    // When the app specifies a LayoutParams#preferredDisplayModeId, in addition to the
    // refresh rate, it also chooses a preferred size (resolution) as part of the selected
    // mode id. The app preference is then translated to APP_REQUEST_BASE_MODE_REFRESH_RATE and
    // optionally to APP_REQUEST_SIZE as well, if a mode id was selected.
    // The system also forces some apps like denylisted app to run at a lower refresh rate.
    // @see android.R.array#config_highRefreshRateBlacklist
    //
    // When summarizing the votes and filtering the allowed display modes, these votes determine
    // which mode id should be the base mode id to be sent to SurfaceFlinger:
    // - APP_REQUEST_BASE_MODE_REFRESH_RATE is used to validate the vote summary. If a summary
    //   includes a base mode refresh rate, but it is not in the refresh rate range, then the
    //   summary is considered invalid so we could drop a lower priority vote and try again.
    // - APP_REQUEST_SIZE is used to filter out display modes of a different size.
    //
    // The preferred refresh rate is set on the main surface of the app outside of
    // DisplayModeDirector.
    // @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded
    static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 5;
    static final int PRIORITY_APP_REQUEST_SIZE = 6;

    // SETTING_PEAK_RENDER_FRAME_RATE has a high priority and will restrict the bounds of the
    // rest of low priority voters. It votes [0, max(PEAK, MIN)]
    static final int PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE = 7;

    // To avoid delay in switching between 60HZ -> 90HZ when activating LHBM, set refresh
    // rate to max value (same as for PRIORITY_UDFPS) on lock screen
    static final int PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE = 8;

    // For concurrent displays we want to limit refresh rate on all displays
    static final int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 9;

    // LOW_POWER_MODE force the render frame rate to [0, 60HZ] if
    // Settings.Global.LOW_POWER_MODE is on.
    static final int PRIORITY_LOW_POWER_MODE = 10;

    // PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
    // higher priority voters' result is a range, it will fix the rate to a single choice.
    // It's used to avoid refresh rate switches in certain conditions which may result in the
    // user seeing the display flickering when the switches occur.
    static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 11;

    // Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
    static final int PRIORITY_SKIN_TEMPERATURE = 12;

    // The proximity sensor needs the refresh rate to be locked in order to function, so this is
    // set to a high priority.
    static final int PRIORITY_PROXIMITY = 13;

    // 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.
    static final int PRIORITY_UDFPS = 14;

    // 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.

    static final int MIN_PRIORITY = PRIORITY_DEFAULT_RENDER_FRAME_RATE;
    static final int MAX_PRIORITY = PRIORITY_UDFPS;

    // 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.
    static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
            PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE;

    /**
     * A value signifying an invalid width or height in a vote.
     */
    static final int INVALID_SIZE = -1;

    /**
     * The requested width of the display in pixels, or INVALID_SIZE;
     */
    public final int width;
    /**
     * The requested height of the display in pixels, or INVALID_SIZE;
     */
    public final int height;
    /**
     * Information about the refresh rate frame rate ranges DM would like to set the display to.
     */
    public final SurfaceControl.RefreshRateRanges refreshRateRanges;

    /**
     * Whether refresh rate switching should be disabled (i.e. the refresh rate range is
     * a single value).
     */
    public final boolean disableRefreshRateSwitching;

    /**
     * The preferred refresh rate selected by the app. It is used to validate that the summary
     * refresh rate ranges include this value, and are not restricted by a lower priority vote.
     */
    public final float appRequestBaseModeRefreshRate;

    static Vote forPhysicalRefreshRates(float minRefreshRate, float maxRefreshRate) {
        return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate, 0,
                Float.POSITIVE_INFINITY,
                minRefreshRate == maxRefreshRate, 0f);
    }

    static Vote forRenderFrameRates(float minFrameRate, float maxFrameRate) {
        return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, minFrameRate,
                maxFrameRate,
                false, 0f);
    }

    static Vote forSize(int width, int height) {
        return new Vote(width, height, 0, Float.POSITIVE_INFINITY, 0, Float.POSITIVE_INFINITY,
                false,
                0f);
    }

    static Vote forDisableRefreshRateSwitching() {
        return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0,
                Float.POSITIVE_INFINITY, true,
                0f);
    }

    static Vote forBaseModeRefreshRate(float baseModeRefreshRate) {
        return new Vote(INVALID_SIZE, INVALID_SIZE, 0, Float.POSITIVE_INFINITY, 0,
                Float.POSITIVE_INFINITY, false,
                baseModeRefreshRate);
    }

    private Vote(int width, int height,
            float minPhysicalRefreshRate,
            float maxPhysicalRefreshRate,
            float minRenderFrameRate,
            float maxRenderFrameRate,
            boolean disableRefreshRateSwitching,
            float baseModeRefreshRate) {
        this.width = width;
        this.height = height;
        this.refreshRateRanges = new SurfaceControl.RefreshRateRanges(
                new SurfaceControl.RefreshRateRange(minPhysicalRefreshRate, maxPhysicalRefreshRate),
                new SurfaceControl.RefreshRateRange(minRenderFrameRate, maxRenderFrameRate));
        this.disableRefreshRateSwitching = disableRefreshRateSwitching;
        this.appRequestBaseModeRefreshRate = baseModeRefreshRate;
    }

    static String priorityToString(int priority) {
        switch (priority) {
            case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE:
                return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE";
            case PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE:
                return "PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE";
            case PRIORITY_APP_REQUEST_SIZE:
                return "PRIORITY_APP_REQUEST_SIZE";
            case PRIORITY_DEFAULT_RENDER_FRAME_RATE:
                return "PRIORITY_DEFAULT_REFRESH_RATE";
            case PRIORITY_FLICKER_REFRESH_RATE:
                return "PRIORITY_FLICKER_REFRESH_RATE";
            case PRIORITY_FLICKER_REFRESH_RATE_SWITCH:
                return "PRIORITY_FLICKER_REFRESH_RATE_SWITCH";
            case PRIORITY_HIGH_BRIGHTNESS_MODE:
                return "PRIORITY_HIGH_BRIGHTNESS_MODE";
            case PRIORITY_PROXIMITY:
                return "PRIORITY_PROXIMITY";
            case PRIORITY_LOW_POWER_MODE:
                return "PRIORITY_LOW_POWER_MODE";
            case PRIORITY_SKIN_TEMPERATURE:
                return "PRIORITY_SKIN_TEMPERATURE";
            case PRIORITY_UDFPS:
                return "PRIORITY_UDFPS";
            case PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE:
                return "PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE";
            case PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE:
                return "PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE";
            case PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE:
                return "PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE";
            case PRIORITY_LAYOUT_LIMITED_FRAME_RATE:
                return "PRIORITY_LAYOUT_LIMITED_FRAME_RATE";
            default:
                return Integer.toString(priority);
        }
    }

    @Override
    public String toString() {
        return "Vote: {"
                + "width: " + width + ", height: " + height
                + ", refreshRateRanges: " + refreshRateRanges
                + ", disableRefreshRateSwitching: " + disableRefreshRateSwitching
                + ", appRequestBaseModeRefreshRate: "  + appRequestBaseModeRefreshRate + "}";
    }
}
+152 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.annotation.NonNull;
import android.annotation.Nullable;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.io.PrintWriter;

class VotesStorage {
    private static final String TAG = "VotesStorage";
    // Special ID used to indicate that given vote is to be applied globally, rather than to a
    // specific display.
    private static final int GLOBAL_ID = -1;

    private boolean mLoggingEnabled;

    private final Listener mListener;

    private final Object mStorageLock = new Object();
    // A map from the display ID to the collection of votes and their priority. The latter takes
    // the form of another map from the priority to the vote itself so that each priority is
    // guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
    @GuardedBy("mStorageLock")
    private final SparseArray<SparseArray<Vote>> mVotesByDisplay = new SparseArray<>();

    VotesStorage(@NonNull Listener listener) {
        mListener = listener;
    }
    /** sets logging enabled/disabled for this class */
    void setLoggingEnabled(boolean loggingEnabled) {
        mLoggingEnabled = loggingEnabled;
    }
    /**
     * gets all votes for specific display, note that global display votes are also added to result
     */
    @NonNull
    SparseArray<Vote> getVotes(int displayId) {
        SparseArray<Vote> votesLocal;
        SparseArray<Vote> globalVotesLocal;
        synchronized (mStorageLock) {
            SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
            votesLocal = displayVotes != null ? displayVotes.clone() : new SparseArray<>();
            SparseArray<Vote> globalVotes = mVotesByDisplay.get(GLOBAL_ID);
            globalVotesLocal = globalVotes != null ? globalVotes.clone() : new SparseArray<>();
        }
        for (int i = 0; i < globalVotesLocal.size(); i++) {
            int priority = globalVotesLocal.keyAt(i);
            if (!votesLocal.contains(priority)) {
                votesLocal.put(priority, globalVotesLocal.valueAt(i));
            }
        }
        return votesLocal;
    }

    /** updates vote storage for all displays */
    void updateGlobalVote(int priority, @Nullable Vote vote) {
        updateVote(GLOBAL_ID, priority, vote);
    }

    /** updates vote storage */
    void updateVote(int displayId, int priority, @Nullable Vote vote) {
        if (mLoggingEnabled) {
            Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
                    + ", priority=" + Vote.priorityToString(priority)
                    + ", vote=" + vote + ")");
        }
        if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
            Slog.w(TAG, "Received a vote with an invalid priority, ignoring:"
                    + " priority=" + Vote.priorityToString(priority)
                    + ", vote=" + vote);
            return;
        }
        SparseArray<Vote> votes;
        synchronized (mStorageLock) {
            if (mVotesByDisplay.contains(displayId)) {
                votes = mVotesByDisplay.get(displayId);
            } else {
                votes = new SparseArray<>();
                mVotesByDisplay.put(displayId, votes);
            }
            if (vote != null) {
                votes.put(priority, vote);
            } else {
                votes.remove(priority);
            }
        }
        if (mLoggingEnabled) {
            Slog.i(TAG, "Updated votes for display=" + displayId + " votes=" + votes);
        }
        mListener.onChanged();
    }

    /** dump class values, for debugging */
    void dump(@NonNull PrintWriter pw) {
        SparseArray<SparseArray<Vote>> votesByDisplayLocal = new SparseArray<>();
        synchronized (mStorageLock) {
            for (int i = 0; i < mVotesByDisplay.size(); i++) {
                votesByDisplayLocal.put(mVotesByDisplay.keyAt(i),
                        mVotesByDisplay.valueAt(i).clone());
            }
        }
        pw.println("  mVotesByDisplay:");
        for (int i = 0; i < votesByDisplayLocal.size(); i++) {
            SparseArray<Vote> votes = votesByDisplayLocal.valueAt(i);
            if (votes.size() == 0) {
                continue;
            }
            pw.println("    " + votesByDisplayLocal.keyAt(i) + ":");
            for (int p = Vote.MAX_PRIORITY; p >= Vote.MIN_PRIORITY; p--) {
                Vote vote = votes.get(p);
                if (vote == null) {
                    continue;
                }
                pw.println("      " + Vote.priorityToString(p) + " -> " + vote);
            }
        }
    }

    @VisibleForTesting
    void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) {
        synchronized (mStorageLock) {
            mVotesByDisplay.clear();
            for (int i = 0; i < votesByDisplay.size(); i++) {
                mVotesByDisplay.put(votesByDisplay.keyAt(i), votesByDisplay.valueAt(i));
            }
        }
    }

    interface Listener {
        void onChanged();
    }
}
+2 −4
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@ 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.server.display.mode.DisplayModeDirector.Vote.INVALID_SIZE;
import static com.android.server.display.mode.Vote.INVALID_SIZE;

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

@@ -94,7 +94,6 @@ import com.android.server.display.DisplayDeviceConfig;
import com.android.server.display.TestUtils;
import com.android.server.display.mode.DisplayModeDirector.BrightnessObserver;
import com.android.server.display.mode.DisplayModeDirector.DesiredDisplayModeSpecs;
import com.android.server.display.mode.DisplayModeDirector.Vote;
import com.android.server.sensors.SensorManagerInternal;
import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -224,8 +223,7 @@ public class DisplayModeDirectorTest {
        assertThat(modeSpecs.appRequest.render.min).isEqualTo(0f);
        assertThat(modeSpecs.appRequest.render.max).isEqualTo(Float.POSITIVE_INFINITY);

        int numPriorities =
                DisplayModeDirector.Vote.MAX_PRIORITY - DisplayModeDirector.Vote.MIN_PRIORITY + 1;
        int numPriorities =  Vote.MAX_PRIORITY - Vote.MIN_PRIORITY + 1;

        // Ensure vote priority works as expected. As we add new votes with higher priority, they
        // should take precedence over lower priority votes.
Loading