Android U Sdr与Hdr混合显示亮度变化流程

405 阅读8分钟

本文档基于Android U aosp代码进行流程梳理

Hdr layer info变化分发流程

#SurfaceFlinger

//frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::postComposition(nsecs_t callTime) {
    std::vector<std::pair<std::shared_ptr<compositionengine::Display>, sp<HdrLayerInfoReporter>>>
        hdrInfoListeners;
    ...
    if (haveNewListeners || mHdrLayerInfoChanged) {
        for (auto& [compositionDisplay, listener] : hdrInfoListeners) {
            HdrLayerInfoReporter::HdrLayerInfo info;
            int32_t maxArea = 0;
            mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
                const auto layerFe = layer->getCompositionEngineLayerFE();
                const frontend::LayerSnapshot& snapshot = *layer->getLayerSnapshot();
                if (snapshot.isVisible &&
                    compositionDisplay->includesLayer(snapshot.outputFilter)) {
                    if (isHdrLayer(snapshot)) {
                        const auto* outputLayer =
                            compositionDisplay->getOutputLayerForLayer(layerFe);
                        if (outputLayer) {
                            const float desiredHdrSdrRatio = snapshot.desiredHdrSdrRatio <= 1.f
                                    ? std::numeric_limits<float>::infinity()
                                    : snapshot.desiredHdrSdrRatio;
                            info.mergeDesiredRatio(desiredHdrSdrRatio);
                            info.numberOfHdrLayers++;
                            const auto displayFrame = outputLayer->getState().displayFrame;
                            const int32_t area = displayFrame.width() * displayFrame.height();
                            if (area > maxArea) {
                                maxArea = area;
                                info.maxW = displayFrame.width();
                                info.maxH = displayFrame.height();
                            }
                        }
                    }
                }
            });
            listener->dispatchHdrLayerInfo(info);
        }
    }
    ...
}

SurfaceFlinger合成时,会去检测是否存在新的listener或者hdr图层信息是否发生了变化,重新组装新的HdrLayerInfo并且进行分发。

#HdrLayerInfoReporter

//frameworks/native/services/surfaceflinger/HdrLayerInfoReporter.cpp
void HdrLayerInfoReporter::dispatchHdrLayerInfo(const HdrLayerInfo& info) {
    ATRACE_CALL();
    std::vector<sp<gui::IHdrLayerInfoListener>> toInvoke;
    {
        std::scoped_lock lock(mMutex);
        toInvoke.reserve(mListeners.size());
        for (auto& [key, it] : mListeners) {
            if (it.lastInfo != info) {
                it.lastInfo = info;
                toInvoke.push_back(it.listener);
            }
        }
    }

    for (const auto& listener : toInvoke) {
        ATRACE_NAME("invoking onHdrLayerInfoChanged");
        listener->onHdrLayerInfoChanged(info.numberOfHdrLayers, info.maxW, info.maxH, info.flags,
                                        info.maxDesiredHdrSdrRatio);
    }
}

#android_view_SurfaceControlHdrLayerInfoListener

//frameworks/base/core/jni/android_view_SurfaceControlHdrLayerInfoListener.cpp
binder::Status onHdrLayerInfoChanged(int numberOfHdrLayers, int maxW, int maxH, int flags,
                                     float maxDesiredHdrSdrRatio) override {
    JNIEnv* env = requireEnv();

    env->CallVoidMethod(mListener, gListenerClassInfo.mOnHdrInfoChanged, mDisplayToken,
                        numberOfHdrLayers, maxW, maxH, flags, maxDesiredHdrSdrRatio);

    if (env->ExceptionCheck()) {
        ALOGE("SurfaceControlHdrLayerInfoListener.onHdrInfoChanged() failed.");
        LOGE_EX(env);
        env->ExceptionClear();
    }
    return binder::Status::ok();
}

#SurfaceControlHdrLayerInfoListener

class HdrListener extends SurfaceControlHdrLayerInfoListener {
    @Override
    public void onHdrInfoChanged(IBinder displayToken, int numberOfHdrLayers,
            int maxW, int maxH, int flags, float maxDesiredHdrSdrRatio) {
    }
}

最后会将HdrLayerInfo通过onHdrInfoChanged进行接收。

HighBrightnessModeController

HighBrightnessModeControllerDisplayPowerController2中进行初始化并持有。通过setHighBrightnessModeMetadata()resetHbmData()hbmController进行参数配置并注册HdrLayerInfo监听。

#onHdrInfoChanged

class HdrListener extends SurfaceControlHdrLayerInfoListener {
    @Override
    public void onHdrInfoChanged(IBinder displayToken, int numberOfHdrLayers,
            int maxW, int maxH, int flags, float maxDesiredHdrSdrRatio) {
        mHandler.post(() -> {
            mIsHdrLayerPresent = numberOfHdrLayers > 0
                    && (float) (maxW * maxH) >= ((float) (mWidth * mHeight)
                               * mHbmData.minimumHdrPercentOfScreen);

            final float candidateDesiredHdrSdrRatio =
                    mIsHdrLayerPresent && mHdrBrightnessCfg != null
                            ? maxDesiredHdrSdrRatio
                            : DEFAULT_MAX_DESIRED_HDR_SDR_RATIO;

            if (candidateDesiredHdrSdrRatio >= 1.0f) {
                mMaxDesiredHdrSdrRatio = candidateDesiredHdrSdrRatio;
            } else {
                Slog.w(TAG, "Ignoring invalid desired HDR/SDR Ratio: "
                        + candidateDesiredHdrSdrRatio);
                mMaxDesiredHdrSdrRatio = DEFAULT_MAX_DESIRED_HDR_SDR_RATIO;
            }

            // Calling the brightness update so that we can recalculate
            // brightness with HDR in mind.
            onBrightnessChanged(mBrightness, mUnthrottledBrightness, mThrottlingReason);
        });
    }
}
  • mIsHdrLayerPresent 代表是否存在hdr图层(hdr图层数量>0;图层面积>屏幕面积*minimumHdrPercentOfScreen
  • mMaxDesiredHdrSdrRatio最大期望Hdr与Sdr比率

#onBrightnessChanged

void onBrightnessChanged(float brightness, float unthrottledBrightness,
        @BrightnessInfo.BrightnessMaxReason int throttlingReason) {
    if (!deviceSupportsHbm()) {
        return;
    }
    mBrightness = brightness;
    mUnthrottledBrightness = unthrottledBrightness;
    mThrottlingReason = throttlingReason;

    // If we are starting or ending a high brightness mode session, store the current
    // session in mRunningStartTimeMillis, or the old one in mEvents.
    final long runningStartTime = mHighBrightnessModeMetadata.getRunningStartTimeMillis();
    final boolean wasHbmDrainingAvailableTime = runningStartTime != -1;
    final boolean shouldHbmDrainAvailableTime = mBrightness > mHbmData.transitionPoint
            && !mIsHdrLayerPresent;
    if (wasHbmDrainingAvailableTime != shouldHbmDrainAvailableTime) {
        final long currentTime = mClock.uptimeMillis();
        if (shouldHbmDrainAvailableTime) {
            mHighBrightnessModeMetadata.setRunningStartTimeMillis(currentTime);
        } else {
            final HbmEvent hbmEvent = new HbmEvent(runningStartTime, currentTime);
            mHighBrightnessModeMetadata.addHbmEvent(hbmEvent);
            mHighBrightnessModeMetadata.setRunningStartTimeMillis(-1);

            if (DEBUG) {
                Slog.d(TAG, "New HBM event: "
                        + mHighBrightnessModeMetadata.getHbmEventQueue().peekFirst());
            }
        }
    }

    recalculateTimeAllowance();
}

private void recalculateTimeAllowance() {
    ...
    // Update the state of the world
    updateHbmMode();
}

private void updateHbmMode() {
    int newHbmMode = calculateHighBrightnessMode();
    updateHbmStats(newHbmMode);
    if (mHbmMode != newHbmMode) {
        mHbmMode = newHbmMode;
        mHbmChangeCallback.run();
    }
}

private int calculateHighBrightnessMode() {
    if (!deviceSupportsHbm()) {
        return BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
    } else if (mIsHdrLayerPresent) {
        return BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR;
    } else if (isCurrentlyAllowed()) {
        return BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
    }

    return BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
}
  • mHighBrightnessModeMetadata 用于记录以往hbm每次的开始时间和结束时间以及当前HBM seesion的开始时间。

calculateHighBrightnessMode方法只判断当前是否存在hdr layer,所以即使没有进入HighBrightnessMode,也同样会run mHbmChangeCallback

DisplayPowerController2

#updatePowerStateInternal

private void updatePowerStateInternal() {
        ...
        // We only want to animate the brightness if it is between 0.0f and 1.0f.
        // brightnessState can contain the values -1.0f and NaN, which we do not want to
        // animate to. To avoid this, we check the value first.
        // If the brightnessState is off (-1.0f) we still want to animate to the minimum
        // brightness (0.0f) to accommodate for LED displays, which can appear bright to the
        // user even when the display is all black. We also clamp here in case some
        // transformations to the brightness have pushed it outside of the currently
        // allowed range.
        float animateValue = clampScreenBrightness(brightnessState);

        // If there are any HDR layers on the screen, we have a special brightness value that we
        // use instead. We still preserve the calculated brightness for Standard Dynamic Range
        // (SDR) layers, but the main brightness value will be the one for HDR.
        float sdrAnimateValue = animateValue;
        // TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be
        // done in HighBrightnessModeController.
        if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
                && (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0
                && (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_LOW_POWER)
                == 0) {
            // We want to scale HDR brightness level with the SDR level
            animateValue = mHbmController.getHdrBrightnessValue();
        }

        final float currentBrightness = mPowerState.getScreenBrightness();
        final float currentSdrBrightness = mPowerState.getSdrScreenBrightness();
        if (BrightnessUtils.isValidBrightnessValue(animateValue)
                && (animateValue != currentBrightness
                || sdrAnimateValue != currentSdrBrightness)) {
            if (initialRampSkip || hasBrightnessBuckets
                    || !isDisplayContentVisible || brightnessIsTemporary) {
                animateScreenBrightness(animateValue, sdrAnimateValue,
                        SCREEN_ANIMATION_RATE_MINIMUM);
            } else {
                boolean isIncreasing = animateValue > currentBrightness;
                final float rampSpeed;
                if (isIncreasing && slowChange) {
                    rampSpeed = mBrightnessRampRateSlowIncrease;
                } else if (isIncreasing && !slowChange) {
                    rampSpeed = mBrightnessRampRateFastIncrease;
                } else if (!isIncreasing && slowChange) {
                    rampSpeed = mBrightnessRampRateSlowDecrease;
                } else {
                    rampSpeed = mBrightnessRampRateFastDecrease;
                }
                animateScreenBrightness(animateValue, sdrAnimateValue, rampSpeed);
            }
        }
        ...
}

#animateScreenBrightness

private void initialize(int displayState) {
    mScreenBrightnessRampAnimator = mInjector.getDualRampAnimator(mPowerState,
        DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT,
        DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT);
}


private void animateScreenBrightness(float target, float sdrTarget, float rate) {
    if (DEBUG) {
        Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
                + ", rate=" + rate);
    }
    if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) {
        Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
        // TODO(b/153319140) remove when we can get this from the above trace invocation
        SystemProperties.set("debug.tracing.screen_brightness", String.valueOf(target));
        noteScreenBrightness(target);
    }
}

RampAnimator

DualRampAnimator#animateTo

/**
 * Starts animating towards the specified values.
 *
 * If this is the first time the property is being set or if the rate is 0,
 * the value jumps directly to the target.
 *
 * @param linearFirstTarget The first target value in linear space.
 * @param linearSecondTarget The second target value in linear space.
 * @param rate The convergence rate in units per second, or 0 to set the value immediately.
 * @return True if either target differs from the previous target.
 */
public boolean animateTo(float linearFirstTarget, float linearSecondTarget, float rate) {
    boolean animationTargetChanged = mFirst.setAnimationTarget(linearFirstTarget, rate);
    animationTargetChanged |= mSecond.setAnimationTarget(linearSecondTarget, rate);
    boolean shouldBeAnimating = isAnimating();

    if (shouldBeAnimating != mAwaitingCallback) {
        if (shouldBeAnimating) {
            mAwaitingCallback = true;
            postAnimationCallback();
        } else if (mAwaitingCallback) {
            mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
                    mAnimationCallback, null);
            mAwaitingCallback = false;
        }
    }
    return animationTargetChanged;
}
  • linearFirstTarget

#setAnimationTarget

/**
 * Sets the animation target and the rate of this ramp animator.
 *
 * If this is the first time the property is being set or if the rate is 0,
 * the value jumps directly to the target.
 *
 * @param targetLinear The target value.
 * @param rate The convergence rate in units per second, or 0 to set the value immediately.
 * @return True if the target differs from the previous target.
 */
boolean setAnimationTarget(float targetLinear, float rate) {
    // Convert the target from the linear into the HLG space.
    final float target = BrightnessUtils.convertLinearToGamma(targetLinear);

    // Immediately jump to the target the first time.
    if (mFirstTime || rate <= 0) {
        if (mFirstTime || target != mCurrentValue) {
            mFirstTime = false;
            mRate = 0;
            mTargetValue = target;
            mCurrentValue = target;
            setPropertyValue(target);
            mAnimating = false;
            return true;
        }
        return false;
    }

    // Adjust the rate so that we do not exceed our maximum animation time.
    if (target > mCurrentValue && mAnimationIncreaseMaxTimeSecs > 0.0f
            && ((target - mCurrentValue) / rate) > mAnimationIncreaseMaxTimeSecs) {
        rate = (target - mCurrentValue) / mAnimationIncreaseMaxTimeSecs;
    } else if (target < mCurrentValue && mAnimationDecreaseMaxTimeSecs > 0.0f
            && ((mCurrentValue - target) / rate) > mAnimationDecreaseMaxTimeSecs) {
        rate = (mCurrentValue - target) / mAnimationDecreaseMaxTimeSecs;
    }

    // Adjust the rate based on the closest target.
    // If a faster rate is specified, then use the new rate so that we converge
    // more rapidly based on the new request.
    // If a slower rate is specified, then use the new rate only if the current
    // value is somewhere in between the new and the old target meaning that
    // we will be ramping in a different direction to get there.
    // Otherwise, continue at the previous rate.
    if (!mAnimating
            || rate > mRate
            || (target <= mCurrentValue && mCurrentValue <= mTargetValue)
            || (mTargetValue <= mCurrentValue && mCurrentValue <= target)) {
        mRate = rate;
    }

    final boolean changed = (mTargetValue != target);
    mTargetValue = target;

    // Start animating.
    if (!mAnimating && target != mCurrentValue) {
        mAnimating = true;
        mAnimatedValue = mCurrentValue;
        mLastFrameTimeNanos = System.nanoTime();
    }

    return changed;
}

rate小于等于0,直接设置;根据mAnimationIncreaseMaxTimeSecs重新调整rate。

#mAnimationCallback

private final Runnable mAnimationCallback = new Runnable() {
    @Override // Choreographer callback
    public void run() {
        long frameTimeNanos = mChoreographer.getFrameTimeNanos();
        mFirst.performNextAnimationStep(frameTimeNanos);
        mSecond.performNextAnimationStep(frameTimeNanos);
        if (isAnimating()) {
            postAnimationCallback();
        } else {
            if (mListener != null) {
                mListener.onAnimationEnd();
            }
            mAwaitingCallback = false;
        }
    }
};

#performNextAnimationStep

void performNextAnimationStep(long frameTimeNanos) {
    final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos) * 0.000000001f;
    mLastFrameTimeNanos = frameTimeNanos;

    // Advance the animated value towards the target at the specified rate
    // and clamp to the target. This gives us the new current value but
    // we keep the animated value around to allow for fractional increments
    // towards the target.
    final float scale = ValueAnimator.getDurationScale();
    if (scale == 0) {
        // Animation off.
        mAnimatedValue = mTargetValue;
    } else {
        final float amount = timeDelta * mRate / scale;
        if (mTargetValue > mCurrentValue) {
            mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
        } else {
            mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
        }
    }
    final float oldCurrentValue = mCurrentValue;
    mCurrentValue = mAnimatedValue;
    if (oldCurrentValue != mCurrentValue) {
        setPropertyValue(mCurrentValue);
    }
    if (mTargetValue == mCurrentValue) {
        mAnimating = false;
    }
}

#setPropertyValue

private void setPropertyValue(float val) {
    final float linearVal = BrightnessUtils.convertGammaToLinear(val);
    mProperty.setValue(mObject, linearVal);
}

DisplayPowerState

#SCREEN_SDR_BRIGHTNESS_FLOAT

public static final FloatProperty<DisplayPowerState> SCREEN_SDR_BRIGHTNESS_FLOAT =
        new FloatProperty<DisplayPowerState>("sdrScreenBrightnessFloat") {
            @Override
            public void setValue(DisplayPowerState object, float value) {
                object.setSdrScreenBrightness(value);
            }

            @Override
            public Float get(DisplayPowerState object) {
                return object.getSdrScreenBrightness();
            }
        };

#setSdrScreenBrightness

public void setSdrScreenBrightness(float brightness) {
    if (mSdrScreenBrightness != brightness) {
        if (DEBUG) {
            Slog.d(TAG, "setSdrScreenBrightness: brightness=" + brightness);
        }

        mSdrScreenBrightness = brightness;
        if (mScreenState != Display.STATE_OFF) {
            mScreenReady = false;
            scheduleScreenUpdate();
        }
    }
}

private void scheduleScreenUpdate() {
    if (!mScreenUpdatePending) {
        mScreenUpdatePending = true;
        postScreenUpdateThreadSafe();
    }
}

private void postScreenUpdateThreadSafe() {
    mHandler.removeCallbacks(mScreenUpdateRunnable);
    mHandler.post(mScreenUpdateRunnable);
}

#mScreenUpdateRunnable

private final Runnable mScreenUpdateRunnable = new Runnable() {
    @Override
    public void run() {
        mScreenUpdatePending = false;

        float brightnessState = mScreenState != Display.STATE_OFF
                && mColorFadeLevel > 0f ? mScreenBrightness : PowerManager.BRIGHTNESS_OFF_FLOAT;
        float sdrBrightnessState = mScreenState != Display.STATE_OFF
                && mColorFadeLevel > 0f
                        ? mSdrScreenBrightness : PowerManager.BRIGHTNESS_OFF_FLOAT;
        if (mPhotonicModulator.setState(mScreenState, brightnessState, sdrBrightnessState)) {
            if (DEBUG) {
                Slog.d(TAG, "Screen ready");
            }
            mScreenReady = true;
            invokeCleanListenerIfNeeded();
        } else {
            if (DEBUG) {
                Slog.d(TAG, "Screen not ready");
            }
        }
    }
};

PhotonicModulator#setState

public boolean setState(int state, float brightnessState, float sdrBrightnessState) {
    synchronized (mLock) {
        boolean stateChanged = state != mPendingState;
        boolean backlightChanged = brightnessState != mPendingBacklight
                || sdrBrightnessState != mPendingSdrBacklight;
        if (stateChanged || backlightChanged) {
            if (DEBUG) {
                Slog.d(TAG, "Requesting new screen state: state="
                        + Display.stateToString(state) + ", backlight=" + brightnessState);
            }

            mPendingState = state;
            mPendingBacklight = brightnessState;
            mPendingSdrBacklight = sdrBrightnessState;
            boolean changeInProgress = mStateChangeInProgress || mBacklightChangeInProgress;
            mStateChangeInProgress = stateChanged || mStateChangeInProgress;
            mBacklightChangeInProgress = backlightChanged || mBacklightChangeInProgress;

            if (!changeInProgress) {
                mLock.notifyAll();
            }
        }
        return !mStateChangeInProgress;
    }
}

PhotonicModulator#run

@Override
public void run() {
    for (;;) {
        // Get pending change.
        final int state;
        final boolean stateChanged;
        final float brightnessState;
        final float sdrBrightnessState;
        final boolean backlightChanged;
        synchronized (mLock) {
            state = mPendingState;
            stateChanged = (state != mActualState);
            brightnessState = mPendingBacklight;
            sdrBrightnessState = mPendingSdrBacklight;
            backlightChanged = brightnessState != mActualBacklight
                    || sdrBrightnessState != mActualSdrBacklight;
            if (!stateChanged) {
                // State changed applied, notify outer class.
                postScreenUpdateThreadSafe();
                mStateChangeInProgress = false;
            }
            if (!backlightChanged) {
                mBacklightChangeInProgress = false;
            }
            boolean valid = state != Display.STATE_UNKNOWN && !Float.isNaN(brightnessState);
            boolean changed = stateChanged || backlightChanged;
            if (!valid || !changed) {
                mStateChangeInProgress = false;
                mBacklightChangeInProgress = false;
                try {
                    mLock.wait();
                } catch (InterruptedException ex) {
                    if (mStopped) {
                        return;
                    }
                }
                continue;
            }
            mActualState = state;
            mActualBacklight = brightnessState;
            mActualSdrBacklight = sdrBrightnessState;
        }

        // Apply pending change.
        if (DEBUG) {
            Slog.d(TAG, "Updating screen state: id=" + mDisplayId +  ", state="
                    + Display.stateToString(state) + ", backlight=" + brightnessState
                    + ", sdrBacklight=" + sdrBrightnessState);
        }
        mBlanker.requestDisplayState(mDisplayId, state, brightnessState,
                sdrBrightnessState);
    }
}

mLock会一直等待,直到setState被调用并且亮度或状态发生变化时mLock才会notifyAll,从而执行mBlanker.requestDisplayState设置display状态。

DisplayManagerService

mDisplayBlanker#requestDisplayState

private final DisplayBlanker mDisplayBlanker = new DisplayBlanker() {
    // Synchronized to avoid race conditions when updating multiple display states.
    @Override
    public synchronized void requestDisplayState(int displayId, int state, float brightness,
            float sdrBrightness) {
        boolean allInactive = true;
        boolean allOff = true;
        final boolean stateChanged;
        synchronized (mSyncRoot) {
            final int index = mDisplayStates.indexOfKey(displayId);
            if (index > -1) {
                final int currentState = mDisplayStates.valueAt(index);
                stateChanged = state != currentState;
                if (stateChanged) {
                    final int size = mDisplayStates.size();
                    for (int i = 0; i < size; i++) {
                        final int displayState = i == index ? state : mDisplayStates.valueAt(i);
                        if (displayState != Display.STATE_OFF) {
                            allOff = false;
                        }
                        if (Display.isActiveState(displayState)) {
                            allInactive = false;
                        }
                        if (!allOff && !allInactive) {
                            break;
                        }
                    }
                }
            } else {
                stateChanged = false;
            }
        }

        // The order of operations is important for legacy reasons.
        if (state == Display.STATE_OFF) {
            requestDisplayStateInternal(displayId, state, brightness, sdrBrightness);
        }

        if (stateChanged) {
            mDisplayPowerCallbacks.onDisplayStateChange(allInactive, allOff);
        }

        if (state != Display.STATE_OFF) {
            requestDisplayStateInternal(displayId, state, brightness, sdrBrightness);
        }
    }
};

#requestDisplayStateInternal

private void requestDisplayStateInternal(int displayId, int state, float brightnessState,
        float sdrBrightnessState) {
    if (state == Display.STATE_UNKNOWN) {
        state = Display.STATE_ON;
    }

    brightnessState = clampBrightness(state, brightnessState);
    sdrBrightnessState = clampBrightness(state, sdrBrightnessState);

    // Update the display state within the lock.
    // Note that we do not need to schedule traversals here although it
    // may happen as a side-effect of displays changing state.
    final Runnable runnable;
    final String traceMessage;
    synchronized (mSyncRoot) {
        final int index = mDisplayStates.indexOfKey(displayId);

        final BrightnessPair brightnessPair =
                index < 0 ? null : mDisplayBrightnesses.valueAt(index);
        if (index < 0 || (mDisplayStates.valueAt(index) == state
                && brightnessPair.brightness == brightnessState
                && brightnessPair.sdrBrightness == sdrBrightnessState)) {
            return; // Display no longer exists or no change.
        }

        if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) {
            traceMessage = Display.stateToString(state)
                       + ", brightness=" + brightnessState
                       + ", sdrBrightness=" + sdrBrightnessState;
            Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_POWER,
                    "requestDisplayStateInternal:" + displayId,
                    traceMessage, displayId);
        }

        mDisplayStates.setValueAt(index, state);
        brightnessPair.brightness = brightnessState;
        brightnessPair.sdrBrightness = sdrBrightnessState;
        runnable = updateDisplayStateLocked(mLogicalDisplayMapper.getDisplayLocked(displayId)
                .getPrimaryDisplayDeviceLocked());
        if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) {
            Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_POWER,
                    "requestDisplayStateInternal:" + displayId, displayId);
        }
    }

    // Setting the display power state can take hundreds of milliseconds
    // to complete so we defer the most expensive part of the work until
    // after we have exited the critical section to avoid blocking other
    // threads for a long time.
    if (runnable != null) {
        runnable.run();
    }
}

#updateDisplayStateLocked

private Runnable updateDisplayStateLocked(DisplayDevice device) {
    // Blank or unblank the display immediately to match the state requested
    // by the display power controller (if known).
    DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
    if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
        final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
        if (display == null) {
            return null;
        }
        final int displayId = display.getDisplayIdLocked();
        final int state = mDisplayStates.get(displayId);

        // Only send a request for display state if display state has already been initialized.
        if (state != Display.STATE_UNKNOWN) {
            final BrightnessPair brightnessPair = mDisplayBrightnesses.get(displayId);
            return device.requestDisplayStateLocked(state, brightnessPair.brightness,
                    brightnessPair.sdrBrightness);
        }
    }
    return null;
}

LocalDisplayAdapter

LocalDisplayDevice#requestDisplayStateLocked

@Override
public Runnable requestDisplayStateLocked(final int state, final float brightnessState,
        final float sdrBrightnessState) {
    // Assume that the brightness is off if the display is being turned off.
    assert state != Display.STATE_OFF || BrightnessSynchronizer.floatEquals(
            brightnessState, PowerManager.BRIGHTNESS_OFF_FLOAT);
    final boolean stateChanged = (mState != state);
    final boolean brightnessChanged =
            !(BrightnessSynchronizer.floatEquals(mBrightnessState, brightnessState)
            && BrightnessSynchronizer.floatEquals(mSdrBrightnessState, sdrBrightnessState));
    if (stateChanged || brightnessChanged) {
        final long physicalDisplayId = mPhysicalDisplayId;
        final IBinder token = getDisplayTokenLocked();
        final int oldState = mState;

        if (stateChanged) {
            mState = state;
            updateDeviceInfoLocked();
        }

        // Defer actually setting the display state until after we have exited
        // the critical section since it can take hundreds of milliseconds
        // to complete.
        return new Runnable() {
            @Override
            public void run() {
                // Exit a suspended state before making any changes.
                int currentState = oldState;
                if (Display.isSuspendedState(oldState)
                        || oldState == Display.STATE_UNKNOWN) {
                    if (!Display.isSuspendedState(state)) {
                        setDisplayState(state);
                        currentState = state;
                    } else if (state == Display.STATE_DOZE_SUSPEND
                            || oldState == Display.STATE_DOZE_SUSPEND) {
                        setDisplayState(Display.STATE_DOZE);
                        currentState = Display.STATE_DOZE;
                    } else if (state == Display.STATE_ON_SUSPEND
                            || oldState == Display.STATE_ON_SUSPEND) {
                        setDisplayState(Display.STATE_ON);
                        currentState = Display.STATE_ON;

                    // If UNKNOWN, we still want to set the initial display state,
                    // otherwise, return early.
                    } else if (oldState != Display.STATE_UNKNOWN) {
                        return; // old state and new state is off
                    }
                }

                // Apply brightness changes given that we are in a non-suspended state.
                if (brightnessChanged) {
                    setDisplayBrightness(brightnessState, sdrBrightnessState);
                    mBrightnessState = brightnessState;
                    mSdrBrightnessState = sdrBrightnessState;
                }

                // Enter the final desired state, possibly suspended.
                if (state != currentState) {
                    setDisplayState(state);
                }
            }
            ...
            private void setDisplayBrightness(float brightnessState,
                    float sdrBrightnessState) {
                // brightnessState includes invalid, off and full range.
                if (Float.isNaN(brightnessState) || Float.isNaN(sdrBrightnessState)) {
                    return;
                }
            
                if (DEBUG) {
                    Slog.d(TAG, "setDisplayBrightness("
                            + "id=" + physicalDisplayId
                            + ", brightnessState=" + brightnessState
                            + ", sdrBrightnessState=" + sdrBrightnessState + ")");
                }
            
                Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness("
                        + "id=" + physicalDisplayId + ", brightnessState="
                        + brightnessState + ", sdrBrightnessState=" + sdrBrightnessState
                        + ")");
                try {
                    final float backlight = brightnessToBacklight(brightnessState);
                    final float sdrBacklight = brightnessToBacklight(sdrBrightnessState);
            
                    final float nits = backlightToNits(backlight);
                    final float sdrNits = backlightToNits(sdrBacklight);
            
                    mBacklightAdapter.setBacklight(sdrBacklight, sdrNits, backlight, nits);
                    Trace.traceCounter(Trace.TRACE_TAG_POWER,
                            "ScreenBrightness",
                            BrightnessSynchronizer.brightnessFloatToInt(brightnessState));
                    Trace.traceCounter(Trace.TRACE_TAG_POWER,
                            "SdrScreenBrightness",
                            BrightnessSynchronizer.brightnessFloatToInt(
                                    sdrBrightnessState));
            
                    handleHdrSdrNitsChanged(nits, sdrNits);
            
                } finally {
                    Trace.traceEnd(Trace.TRACE_TAG_POWER);
                }
            }
            ...
        };
    }        
}

BacklightAdapter#setBacklight

// Set backlight within min and max backlight values
void setBacklight(float sdrBacklight, float sdrNits, float backlight, float nits) {
    if (mUseSurfaceControlBrightness || mForceSurfaceControl) {
        if (BrightnessSynchronizer.floatEquals(
                sdrBacklight, PowerManager.BRIGHTNESS_INVALID_FLOAT)) {
            mSurfaceControlProxy.setDisplayBrightness(mDisplayToken, backlight);
        } else {
            mSurfaceControlProxy.setDisplayBrightness(mDisplayToken, sdrBacklight, sdrNits,
                    backlight, nits);
        }
    } else if (mBacklight != null) {
        mBacklight.setBrightness(backlight);
    }
}

SurfaceControlProxy#setDisplayBrightness

public boolean setDisplayBrightness(IBinder displayToken, float sdrBacklight,
        float sdrNits, float displayBacklight, float displayNits) {
    return SurfaceControl.setDisplayBrightness(displayToken, sdrBacklight, sdrNits,
            displayBacklight, displayNits);
}

SurfaceControl

#setDisplayBrightness

public static boolean setDisplayBrightness(IBinder displayToken, float sdrBrightness,
        float sdrBrightnessNits, float displayBrightness, float displayBrightnessNits) {
    Objects.requireNonNull(displayToken);
    if (Float.isNaN(displayBrightness) || displayBrightness > 1.0f
            || (displayBrightness < 0.0f && displayBrightness != -1.0f)) {
        throw new IllegalArgumentException("displayBrightness must be a number between 0.0f "
                + " and 1.0f, or -1 to turn the backlight off: " + displayBrightness);
    }
    if (Float.isNaN(sdrBrightness) || sdrBrightness > 1.0f
            || (sdrBrightness < 0.0f && sdrBrightness != -1.0f)) {
        throw new IllegalArgumentException("sdrBrightness must be a number between 0.0f "
                + "and 1.0f, or -1 to turn the backlight off: " + sdrBrightness);
    }
    return nativeSetDisplayBrightness(displayToken, sdrBrightness, sdrBrightnessNits,
            displayBrightness, displayBrightnessNits);
}

#nativeSetDisplayBrightness

private static native boolean nativeSetDisplayBrightness(IBinder displayToken,
        float sdrBrightness, float sdrBrightnessNits, float displayBrightness,
        float displayBrightnessNits);