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

Commit 816b80e8 authored by Santos Cordon's avatar Santos Cordon
Browse files

High Brightness Mode (HBM) Refresh Rate Limiting.

Defines refresh-rate limits for high-brightness-mode in display-config
xml files. DisplayModeDirector listens to HBM status changes from
DisplayManager and applies the pre-defined refresh-rate limits when HBM
is enabled.

Test: Manual, verify a Refresh Rate vote when HBM is enabled, and a
      null-vote when disabled.
Test: atest com.android.server.display
Bug: 181955334
Change-Id: Id642cbff96fca9ce79a50d6cd97e75cd6061ac74
parent 8a3115ba
Loading
Loading
Loading
Loading
+46 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.hardware.display;

import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Point;
import android.hardware.SensorManager;
@@ -29,6 +30,9 @@ import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Objects;

/**
@@ -37,6 +41,16 @@ import java.util.Objects;
 * @hide Only for use within the system server.
 */
public abstract class DisplayManagerInternal {

    @IntDef(prefix = {"REFRESH_RATE_LIMIT_"}, value = {
            REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface RefreshRateLimitType {}

    /** Refresh rate should be limited when High Brightness Mode is active. */
    public static final int REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE = 1;

    /**
     * Called by the power manager to initialize power management facilities.
     */
@@ -296,9 +310,10 @@ public abstract class DisplayManagerInternal {
    public abstract int getRefreshRateSwitchingType();

    /**
     * TODO: b/191384041 - Replace this with getRefreshRateLimitations()
     * Return the refresh rate restriction for the specified display and sensor pairing. If the
     * specified sensor is identified as an associated sensor in the specified display's
     * display-device-config file, then return any refresh rate restrictions that it might specify.
     * display-device-config file, then return any refresh rate restrictions that it might define.
     * If no restriction is specified, or the sensor is not associated with the display, then null
     * will be returned.
     *
@@ -312,6 +327,15 @@ public abstract class DisplayManagerInternal {
    public abstract RefreshRateRange getRefreshRateForDisplayAndSensor(
            int displayId, String name, String type);

    /**
     * Returns a list of various refresh rate limitations for the specified display.
     *
     * @param displayId The display to get limitations for.
     *
     * @return a list of {@link RefreshRateLimitation}s describing the various limits.
     */
    public abstract List<RefreshRateLimitation> getRefreshRateLimitations(int displayId);

    /**
     * Describes the requested power state of the display.
     *
@@ -613,4 +637,25 @@ public abstract class DisplayManagerInternal {
            return "(" + min + " " + max + ")";
        }
    }

    /**
     * Describes a limitation on a display's refresh rate. Includes the allowed refresh rate
     * range as well as information about when it applies, such as high-brightness-mode.
     */
    public static final class RefreshRateLimitation {
        @RefreshRateLimitType public int type;

        /** The range the that refresh rate should be limited to. */
        public RefreshRateRange range;

        public RefreshRateLimitation(@RefreshRateLimitType int type, float min, float max) {
            this.type = type;
            range = new RefreshRateRange(min, max);
        }

        @Override
        public String toString() {
            return "RefreshRateLimitation(" + type + ": " + range + ")";
        }
    }
}
+17 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.server.display;
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
import android.os.Environment;
import android.os.PowerManager;
import android.text.TextUtils;
@@ -86,6 +88,9 @@ public class DisplayDeviceConfig {
    // The details of the proximity sensor associated with this display.
    private final SensorData mProximitySensor = new SensorData();

    private final List<RefreshRateLimitation> mRefreshRateLimitations =
            new ArrayList<>(2 /*initialCapacity*/);

    // Nits and backlight values that are loaded from either the display device config file, or
    // config.xml. These are the raw values and just used for the dumpsys
    private float[] mRawNits;
@@ -306,6 +311,10 @@ public class DisplayDeviceConfig {
        return hbmData;
    }

    public List<RefreshRateLimitation> getRefreshRateLimitations() {
        return mRefreshRateLimitations;
    }

    @Override
    public String toString() {
        String str = "DisplayDeviceConfig{"
@@ -329,6 +338,7 @@ public class DisplayDeviceConfig {
                + ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease
                + ", mAmbientLightSensor=" + mAmbientLightSensor
                + ", mProximitySensor=" + mProximitySensor
                + ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
                + "}";
        return str;
    }
@@ -647,6 +657,13 @@ public class DisplayDeviceConfig {
            mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000;
            mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000;
            mHbmData.timeMinMillis = hbmTiming.getTimeMinSecs_all().longValue() * 1000;
            final RefreshRateRange rr = hbm.getRefreshRate_all();
            if (rr != null) {
                final float min = rr.getMinimum().floatValue();
                final float max = rr.getMaximum().floatValue();
                mRefreshRateLimitations.add(new RefreshRateLimitation(
                        DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, min, max));
            }
        }
    }

+20 −0
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayGroupListener;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
@@ -130,6 +131,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
@@ -2111,6 +2113,11 @@ public final class DisplayManagerService extends SystemService {
                DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED);
    }

    private DisplayDevice getDeviceForDisplayLocked(int displayId) {
        final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
        return display == null ? null : display.getPrimaryDisplayDeviceLocked();
    }

    private final class DisplayManagerHandler extends Handler {
        public DisplayManagerHandler(Looper looper) {
            super(looper, null, true /*async*/);
@@ -3295,6 +3302,19 @@ public final class DisplayManagerService extends SystemService {
            }
            return null;
        }

        @Override
        public List<RefreshRateLimitation> getRefreshRateLimitations(int displayId) {
            final DisplayDeviceConfig config;
            synchronized (mSyncRoot) {
                final DisplayDevice device = getDeviceForDisplayLocked(displayId);
                if (device == null) {
                    return null;
                }
                config = device.getDisplayDeviceConfig();
            }
            return config.getRefreshRateLimitations();
        }
    }

    class DesiredDisplayModeSpecsObserver
+133 −18
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.display;

import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
@@ -26,8 +28,10 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.net.Uri;
@@ -102,6 +106,7 @@ public class DisplayModeDirector {
    private final DisplayObserver mDisplayObserver;
    private final UdfpsObserver mUdfpsObserver;
    private final SensorObserver mSensorObserver;
    private final HbmObserver mHbmObserver;
    private final DeviceConfigInterface mDeviceConfig;
    private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;

@@ -127,7 +132,7 @@ public class DisplayModeDirector {
    private int mModeSwitchingType = DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS;

    public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
        this(context, handler, new RealInjector());
        this(context, handler, new RealInjector(context));
    }

    public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
@@ -143,11 +148,13 @@ public class DisplayModeDirector {
        mDisplayObserver = new DisplayObserver(context, handler);
        mBrightnessObserver = new BrightnessObserver(context, handler);
        mUdfpsObserver = new UdfpsObserver();
        mSensorObserver = new SensorObserver(context, (displayId, priority, vote) -> {
        final BallotBox ballotBox = (displayId, priority, vote) -> {
            synchronized (mLock) {
                updateVoteLocked(displayId, priority, vote);
            }
        });
        };
        mSensorObserver = new SensorObserver(context, ballotBox);
        mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler());
        mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
        mDeviceConfig = injector.getDeviceConfig();
        mAlwaysRespectAppRequest = false;
@@ -165,6 +172,7 @@ public class DisplayModeDirector {
        mDisplayObserver.observe();
        mBrightnessObserver.observe(sensorManager);
        mSensorObserver.observe();
        mHbmObserver.observe();
        synchronized (mLock) {
            // We may have a listener already registered before the call to start, so go ahead and
            // notify them to pick up our newly initialized state.
@@ -596,6 +604,7 @@ public class DisplayModeDirector {
            mBrightnessObserver.dumpLocked(pw);
            mUdfpsObserver.dumpLocked(pw);
            mSensorObserver.dumpLocked(pw);
            mHbmObserver.dumpLocked(pw);
        }
    }

@@ -938,13 +947,16 @@ public class DisplayModeDirector {
        // user seeing the display flickering when the switches occur.
        public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 8;

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

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

        // 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 = 10;
        public static final int PRIORITY_UDFPS = 11;

        // 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.
@@ -1021,29 +1033,30 @@ public class DisplayModeDirector {

        public 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_MAX_REFRESH_RATE:
                    return "PRIORITY_APP_REQUEST_MAX_REFRESH_RATE";
                case PRIORITY_APP_REQUEST_SIZE:
                    return "PRIORITY_APP_REQUEST_SIZE";
                case PRIORITY_DEFAULT_REFRESH_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_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_BASE_MODE_REFRESH_RATE:
                    return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE";
                case PRIORITY_APP_REQUEST_SIZE:
                    return "PRIORITY_APP_REQUEST_SIZE";
                case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
                    return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
                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_UDFPS:
                    return "PRIORITY_UDFPS";
                case PRIORITY_PROXIMITY:
                    return "PRIORITY_PROXIMITY";

                case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
                    return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
                case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
                    return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
                default:
                    return Integer.toString(priority);
            }
@@ -2155,6 +2168,75 @@ public class DisplayModeDirector {
        }
    }

    /**
     * Listens to DisplayManager for HBM status and applies any refresh-rate restrictions for
     * HBM that are associated with that display. Restrictions are retrieved from
     * DisplayManagerInternal but originate in the display-device-config file.
     */
    private static class HbmObserver implements DisplayManager.DisplayListener {
        private final BallotBox mBallotBox;
        private final Handler mHandler;
        private final SparseBooleanArray mHbmEnabled = new SparseBooleanArray();
        private final Injector mInjector;

        private DisplayManagerInternal mDisplayManagerInternal;

        HbmObserver(Injector injector, BallotBox ballotBox, Handler handler) {
            mInjector = injector;
            mBallotBox = ballotBox;
            mHandler = handler;
        }

        public void observe() {
            mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
            mInjector.registerDisplayListener(this, mHandler,
                    DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
                    | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
        }

        @Override
        public void onDisplayAdded(int displayId) {}

        @Override
        public void onDisplayRemoved(int displayId) {
            mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null);
        }

        @Override
        public void onDisplayChanged(int displayId) {
            final BrightnessInfo info = mInjector.getBrightnessInfo(displayId);
            if (info == null) {
                // Display no longer there. Assume we'll get an onDisplayRemoved very soon.
                return;
            }
            final boolean isHbmEnabled =
                    info.highBrightnessMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
            if (isHbmEnabled == mHbmEnabled.get(displayId)) {
                // no change, ignore.
                return;
            }
            Vote vote = null;
            mHbmEnabled.put(displayId, isHbmEnabled);
            if (isHbmEnabled) {
                final List<RefreshRateLimitation> limits =
                        mDisplayManagerInternal.getRefreshRateLimitations(displayId);
                for (int i = 0; limits != null && i < limits.size(); i++) {
                    final RefreshRateLimitation limitation = limits.get(i);
                    if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
                        vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max);
                        break;
                    }
                }
            }
            mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote);
        }

        void dumpLocked(PrintWriter pw) {
            pw.println("   HbmObserver");
            pw.println("     mHbmEnabled: " + mHbmEnabled);
        }
    }

    private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
        public DeviceConfigDisplaySettings() {
        }
@@ -2309,10 +2391,21 @@ public class DisplayModeDirector {

        void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
                @NonNull ContentObserver observer);

        void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
                Handler handler, long flags);

        BrightnessInfo getBrightnessInfo(int displayId);
    }

    @VisibleForTesting
    static class RealInjector implements Injector {
        private final Context mContext;
        private DisplayManager mDisplayManager;

        RealInjector(Context context) {
            mContext = context;
        }

        @Override
        @NonNull
@@ -2339,6 +2432,28 @@ public class DisplayModeDirector {
            cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
                    observer, UserHandle.USER_SYSTEM);
        }

        @Override
        public void registerDisplayListener(DisplayManager.DisplayListener listener,
                Handler handler, long flags) {
            getDisplayManager().registerDisplayListener(listener, handler, flags);
        }

        @Override
        public BrightnessInfo getBrightnessInfo(int displayId) {
            final Display display = getDisplayManager().getDisplay(displayId);
            if (display != null) {
                return display.getBrightnessInfo();
            }
            return null;
        }

        private DisplayManager getDisplayManager() {
            if (mDisplayManager == null) {
                mDisplayManager = mContext.getSystemService(DisplayManager.class);
            }
            return mDisplayManager;
        }
    }

    interface BallotBox {
+4 −0
Original line number Diff line number Diff line
@@ -77,6 +77,10 @@
                <xs:annotation name="final"/>
            </xs:element>
            <xs:element name="timing" type="hbmTiming" minOccurs="1" maxOccurs="1"/>
            <xs:element type="refreshRateRange" name="refreshRate" minOccurs="0" maxOccurs="1">
                <xs:annotation name="nullable"/>
                <xs:annotation name="final"/>
            </xs:element>
        </xs:all>
        <xs:attribute name="enabled" type="xs:boolean" use="optional"/>
    </xs:complexType>
Loading