本文档基于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
HighBrightnessModeController在DisplayPowerController2中进行初始化并持有。通过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);