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

Commit ce712dee authored by John Reck's avatar John Reck Committed by Android (Google) Code Review
Browse files

Merge "Add Display#getHdrSdrRatio"

parents 8bb72213 c86397fd
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -541,7 +541,8 @@ public final class DisplayManager {
            EVENT_FLAG_DISPLAY_ADDED,
            EVENT_FLAG_DISPLAY_CHANGED,
            EVENT_FLAG_DISPLAY_REMOVED,
            EVENT_FLAG_DISPLAY_BRIGHTNESS
            EVENT_FLAG_DISPLAY_BRIGHTNESS,
            EVENT_FLAG_HDR_SDR_RATIO_CHANGED
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface EventsMask {}
@@ -584,6 +585,19 @@ public final class DisplayManager {
     */
    public static final long EVENT_FLAG_DISPLAY_BRIGHTNESS = 1L << 3;

    /**
     * Event flag to register for a display's hdr/sdr ratio changes. This notification is sent
     * through the {@link DisplayListener#onDisplayChanged} callback method. New hdr/sdr
     * values can be retrieved via {@link Display#getHdrSdrRatio()}.
     *
     * Requires that {@link Display#isHdrSdrRatioAvailable()} is true.
     *
     * @see #registerDisplayListener(DisplayListener, Handler, long)
     *
     * @hide
     */
    public static final long EVENT_FLAG_HDR_SDR_RATIO_CHANGED = 1L << 4;

    /** @hide */
    public DisplayManager(Context context) {
        mContext = context;
+53 −19
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.hardware.graphics.common.DisplayDecorationSupport;
import android.media.projection.IMediaProjection;
import android.media.projection.MediaProjection;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
@@ -56,11 +57,12 @@ import com.android.internal.annotations.VisibleForTesting;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Manager communication with the display manager service on behalf of
@@ -82,11 +84,12 @@ public final class DisplayManagerGlobal {
    // orientation change before the display info cache has actually been invalidated.
    private static final boolean USE_CACHE = false;

    @IntDef(prefix = {"SWITCHING_TYPE_"}, value = {
    @IntDef(prefix = {"EVENT_DISPLAY_"}, flag = true, value = {
            EVENT_DISPLAY_ADDED,
            EVENT_DISPLAY_CHANGED,
            EVENT_DISPLAY_REMOVED,
            EVENT_DISPLAY_BRIGHTNESS_CHANGED
            EVENT_DISPLAY_BRIGHTNESS_CHANGED,
            EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface DisplayEvent {}
@@ -95,6 +98,7 @@ public final class DisplayManagerGlobal {
    public static final int EVENT_DISPLAY_CHANGED = 2;
    public static final int EVENT_DISPLAY_REMOVED = 3;
    public static final int EVENT_DISPLAY_BRIGHTNESS_CHANGED = 4;
    public static final int EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED = 5;

    @UnsupportedAppUsage
    private static DisplayManagerGlobal sInstance;
@@ -109,7 +113,8 @@ public final class DisplayManagerGlobal {

    private DisplayManagerCallback mCallback;
    private @EventsMask long mRegisteredEventsMask = 0;
    private final ArrayList<DisplayListenerDelegate> mDisplayListeners = new ArrayList<>();
    private final CopyOnWriteArrayList<DisplayListenerDelegate> mDisplayListeners =
            new CopyOnWriteArrayList<>();

    private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<>();
    private final ColorSpace mWideColorSpace;
@@ -315,6 +320,19 @@ public final class DisplayManagerGlobal {
     */
    public void registerDisplayListener(@NonNull DisplayListener listener,
            @Nullable Handler handler, @EventsMask long eventsMask) {
        Looper looper = getLooperForHandler(handler);
        Handler springBoard = new Handler(looper);
        registerDisplayListener(listener, new HandlerExecutor(springBoard), eventsMask);
    }

    /**
     * Register a listener for display-related changes.
     *
     * @param listener The listener that will be called when display changes occur.
     * @param executor Executor for the thread that will be receiving the callbacks. Cannot be null.
     */
    public void registerDisplayListener(@NonNull DisplayListener listener,
            @NonNull Executor executor, @EventsMask long eventsMask) {
        if (listener == null) {
            throw new IllegalArgumentException("listener must not be null");
        }
@@ -326,8 +344,7 @@ public final class DisplayManagerGlobal {
        synchronized (mLock) {
            int index = findDisplayListenerLocked(listener);
            if (index < 0) {
                Looper looper = getLooperForHandler(handler);
                mDisplayListeners.add(new DisplayListenerDelegate(listener, looper, eventsMask));
                mDisplayListeners.add(new DisplayListenerDelegate(listener, executor, eventsMask));
                registerCallbackIfNeededLocked();
            } else {
                mDisplayListeners.get(index).setEventsMask(eventsMask);
@@ -408,6 +425,7 @@ public final class DisplayManagerGlobal {
    }

    private void handleDisplayEvent(int displayId, @DisplayEvent int event) {
        final DisplayInfo info;
        synchronized (mLock) {
            if (USE_CACHE) {
                mDisplayInfoCache.remove(displayId);
@@ -417,11 +435,7 @@ public final class DisplayManagerGlobal {
                }
            }

            final int numListeners = mDisplayListeners.size();
            DisplayInfo info = getDisplayInfo(displayId);
            for (int i = 0; i < numListeners; i++) {
                mDisplayListeners.get(i).sendDisplayEvent(displayId, event, info);
            }
            info = getDisplayInfoLocked(displayId);
            if (event == EVENT_DISPLAY_CHANGED && mDispatchNativeCallbacks) {
                // Choreographer only supports a single display, so only dispatch refresh rate
                // changes for the default display.
@@ -438,6 +452,11 @@ public final class DisplayManagerGlobal {
                }
            }
        }
        // Accepting an Executor means the listener may be synchronously invoked, so we must
        // not be holding mLock when we do so
        for (DisplayListenerDelegate listener : mDisplayListeners) {
            listener.sendDisplayEvent(displayId, event, info);
        }
    }

    public void startWifiDisplayScan() {
@@ -1088,34 +1107,42 @@ public final class DisplayManagerGlobal {
        }
    }

    private static final class DisplayListenerDelegate extends Handler {
    private static final class DisplayListenerDelegate {
        public final DisplayListener mListener;
        public volatile long mEventsMask;

        private final DisplayInfo mDisplayInfo = new DisplayInfo();
        private final Executor mExecutor;
        private AtomicLong mGenerationId = new AtomicLong(1);

        DisplayListenerDelegate(DisplayListener listener, @NonNull Looper looper,
        DisplayListenerDelegate(DisplayListener listener, @NonNull Executor executor,
                @EventsMask long eventsMask) {
            super(looper, null, true /*async*/);
            mExecutor = executor;
            mListener = listener;
            mEventsMask = eventsMask;
        }

        public void sendDisplayEvent(int displayId, @DisplayEvent int event, DisplayInfo info) {
            Message msg = obtainMessage(event, displayId, 0, info);
            sendMessage(msg);
            long generationId = mGenerationId.get();
            Message msg = Message.obtain(null, event, displayId, 0, info);
            mExecutor.execute(() -> {
                // If the generation id's don't match we were canceled but still need to recycle()
                if (generationId == mGenerationId.get()) {
                    handleMessage(msg);
                }
                msg.recycle();
            });
        }

        public void clearEvents() {
            removeCallbacksAndMessages(null);
            mGenerationId.incrementAndGet();
        }

        public void setEventsMask(@EventsMask long newEventsMask) {
            mEventsMask = newEventsMask;
        }

        @Override
        public void handleMessage(Message msg) {
        private void handleMessage(Message msg) {
            if (DEBUG) {
                Trace.beginSection(
                        "DisplayListenerDelegate(" + eventToString(msg.what)
@@ -1147,6 +1174,11 @@ public final class DisplayManagerGlobal {
                        mListener.onDisplayRemoved(msg.arg1);
                    }
                    break;
                case EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
                    if ((mEventsMask & DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0) {
                        mListener.onDisplayChanged(msg.arg1);
                    }
                    break;
            }
            if (DEBUG) {
                Trace.endSection();
@@ -1268,6 +1300,8 @@ public final class DisplayManagerGlobal {
                return "REMOVED";
            case EVENT_DISPLAY_BRIGHTNESS_CHANGED:
                return "BRIGHTNESS_CHANGED";
            case EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
                return "HDR_SDR_RATIO_CHANGED";
        }
        return "UNKNOWN";
    }
+129 −0
Original line number Diff line number Diff line
@@ -56,6 +56,8 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
 * Provides information about the size and density of a logical display.
@@ -111,6 +113,8 @@ public final class Display {
    private int mCachedAppWidthCompat;
    private int mCachedAppHeightCompat;

    private ArrayList<HdrSdrRatioListenerWrapper> mHdrSdrRatioListeners = new ArrayList<>();

    /**
     * The default Display id, which is the id of the primary display assuming there is one.
     */
@@ -1291,6 +1295,102 @@ public final class Display {
        }
    }

    /**
     * @return Whether the display supports reporting an hdr/sdr ratio. If this is false,
     *         {@link #getHdrSdrRatio()} will always be 1.0f
     * @hide
     * TODO: make public
     */
    public boolean isHdrSdrRatioAvailable() {
        synchronized (mLock) {
            updateDisplayInfoLocked();
            return !Float.isNaN(mDisplayInfo.hdrSdrRatio);
        }
    }

    /**
     * @return The current hdr/sdr ratio expressed as the ratio of targetHdrPeakBrightnessInNits /
     *         targetSdrWhitePointInNits. If {@link #isHdrSdrRatioAvailable()} is false, this
     *         always returns 1.0f.
     *
     * @hide
     * TODO: make public
     */
    public float getHdrSdrRatio() {
        synchronized (mLock) {
            updateDisplayInfoLocked();
            return Float.isNaN(mDisplayInfo.hdrSdrRatio)
                    ? 1.0f : mDisplayInfo.hdrSdrRatio;
        }
    }

    private int findHdrSdrRatioListenerLocked(Consumer<Display> listener) {
        for (int i = 0; i < mHdrSdrRatioListeners.size(); i++) {
            final HdrSdrRatioListenerWrapper wrapper = mHdrSdrRatioListeners.get(i);
            if (wrapper.mListener == listener) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Registers a listener that will be invoked whenever the display's hdr/sdr ratio has changed.
     * After receiving the callback on the specified Executor, call {@link #getHdrSdrRatio()} to
     * get the updated value.
     * If {@link #isHdrSdrRatioAvailable()} is false, then an IllegalStateException will be thrown
     *
     * @see #unregisterHdrSdrRatioChangedListener(Consumer)
     * @param executor The executor to invoke the listener on
     * @param listener The listener to invoke when the HDR/SDR ratio changes
     * @throws IllegalStateException if {@link #isHdrSdrRatioAvailable()} is false
     * @hide
     * TODO: Make public
     */
    public void registerHdrSdrRatioChangedListener(@NonNull Executor executor,
            @NonNull Consumer<Display> listener) {
        if (!isHdrSdrRatioAvailable()) {
            throw new IllegalStateException("HDR/SDR ratio changed not available");
        }
        HdrSdrRatioListenerWrapper toRegister = null;
        synchronized (mLock) {
            if (findHdrSdrRatioListenerLocked(listener) == -1) {
                toRegister = new HdrSdrRatioListenerWrapper(listener);
                mHdrSdrRatioListeners.add(toRegister);
            } // else already listening, don't do anything
        }
        if (toRegister != null) {
            // Although we only care about the HDR/SDR ratio changing, that can also come in the
            // form of the larger DISPLAY_CHANGED event
            mGlobal.registerDisplayListener(toRegister, executor,
                    DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED
                            | DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
        }

    }

    /**
     * @param listener  The previously
     *                  {@link #registerHdrSdrRatioChangedListener(Executor, Consumer) registered}
     *                  hdr/sdr ratio listener to remove.
     *
     * @see #registerHdrSdrRatioChangedListener(Executor, Consumer)
     * @hide
     * TODO: Make public
     */
    public void unregisterHdrSdrRatioChangedListener(Consumer<Display> listener) {
        HdrSdrRatioListenerWrapper toRemove = null;
        synchronized (mLock) {
            int index = findHdrSdrRatioListenerLocked(listener);
            if (index != -1) {
                toRemove = mHdrSdrRatioListeners.remove(index);
            }
        }
        if (toRemove != null) {
            mGlobal.unregisterDisplayListener(toRemove);
        }
    }

    /**
     * Sets the default {@link Display.Mode} to use for the display.  The display mode includes
     * preference for resolution and refresh rate.
@@ -2528,4 +2628,33 @@ public final class Display {
            }
        }
    }

    private class HdrSdrRatioListenerWrapper implements DisplayManager.DisplayListener {
        Consumer<Display> mListener;
        float mLastReportedRatio = 1.f;

        private HdrSdrRatioListenerWrapper(Consumer<Display> listener) {
            mListener = listener;
        }

        @Override
        public void onDisplayAdded(int displayId) {
            // don't care
        }

        @Override
        public void onDisplayRemoved(int displayId) {
            // don't care
        }

        @Override
        public void onDisplayChanged(int displayId) {
            if (displayId == getDisplayId()) {
                float newRatio = getHdrSdrRatio();
                if (newRatio != mLastReportedRatio) {
                    mListener.accept(Display.this);
                }
            }
        }
    }
}
+20 −1
Original line number Diff line number Diff line
@@ -39,6 +39,8 @@ import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.proto.ProtoOutputStream;

import com.android.internal.display.BrightnessSynchronizer;

import java.util.Arrays;
import java.util.Objects;

@@ -340,6 +342,13 @@ public final class DisplayInfo implements Parcelable {
    @Nullable
    public SurfaceControl.RefreshRateRange layoutLimitedRefreshRate;

    /**
     * The current hdr/sdr ratio for the display. If the display doesn't support hdr/sdr ratio
     * queries then this is NaN
     */
    public float hdrSdrRatio = Float.NaN;


    public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
        @Override
        public DisplayInfo createFromParcel(Parcel source) {
@@ -415,7 +424,8 @@ public final class DisplayInfo implements Parcelable {
                && Objects.equals(roundedCorners, other.roundedCorners)
                && installOrientation == other.installOrientation
                && Objects.equals(displayShape, other.displayShape)
                && Objects.equals(layoutLimitedRefreshRate, other.layoutLimitedRefreshRate);
                && Objects.equals(layoutLimitedRefreshRate, other.layoutLimitedRefreshRate)
                && BrightnessSynchronizer.floatEquals(hdrSdrRatio, other.hdrSdrRatio);
    }

    @Override
@@ -471,6 +481,7 @@ public final class DisplayInfo implements Parcelable {
        installOrientation = other.installOrientation;
        displayShape = other.displayShape;
        layoutLimitedRefreshRate = other.layoutLimitedRefreshRate;
        hdrSdrRatio = other.hdrSdrRatio;
    }

    public void readFromParcel(Parcel source) {
@@ -532,6 +543,7 @@ public final class DisplayInfo implements Parcelable {
        installOrientation = source.readInt();
        displayShape = source.readTypedObject(DisplayShape.CREATOR);
        layoutLimitedRefreshRate = source.readTypedObject(SurfaceControl.RefreshRateRange.CREATOR);
        hdrSdrRatio = source.readFloat();
    }

    @Override
@@ -591,6 +603,7 @@ public final class DisplayInfo implements Parcelable {
        dest.writeInt(installOrientation);
        dest.writeTypedObject(displayShape, flags);
        dest.writeTypedObject(layoutLimitedRefreshRate, flags);
        dest.writeFloat(hdrSdrRatio);
    }

    @Override
@@ -852,6 +865,12 @@ public final class DisplayInfo implements Parcelable {
        sb.append(Surface.rotationToString(installOrientation));
        sb.append(", layoutLimitedRefreshRate ");
        sb.append(layoutLimitedRefreshRate);
        sb.append(", hdrSdrRatio ");
        if (Float.isNaN(hdrSdrRatio)) {
            sb.append("not_available");
        } else {
            sb.append(hdrSdrRatio);
        }
        sb.append("}");
        return sb.toString();
    }
+2 −2
Original line number Diff line number Diff line
@@ -450,7 +450,7 @@ public class DisplayDeviceConfig {
    // so -2 is used instead
    private static final float INVALID_BRIGHTNESS_IN_CONFIG = -2f;

    private static final float NITS_INVALID = -1;
    static final float NITS_INVALID = -1;

    // Length of the ambient light horizon used to calculate the long term estimate of ambient
    // light.
@@ -840,7 +840,7 @@ public class DisplayDeviceConfig {
    /**
     * Calculates the nits value for the specified backlight value if a mapping exists.
     *
     * @return The mapped nits or 0 if no mapping exits.
     * @return The mapped nits or {@link #NITS_INVALID} if no mapping exits.
     */
    public float getNitsFromBacklight(float backlight) {
        if (mBacklightToNitsSpline == null) {
Loading