Android6.0 亮屏滅屏流程(DisplayPowerControler、WMS)(二)亮度設定
上一篇部落格我們主要分析了在setScreenState中呼叫PhoneWindowManager的一些流程,在setScreenState中先是呼叫了DisplayPowerState的setScreenState函式。上篇部落格我們沒有分析,這篇部落格我們先從這個函式開始分析,主要分析下亮度的設定流程。
public void setScreenState(int state) { if (mScreenState != state) { if (DEBUG) { Slog.d(TAG, "setScreenState: state=" + state); } mScreenState = state; mScreenReady = false; scheduleScreenUpdate(); } }
scheduleScreenUpdate主要通過訊息方式,最後呼叫到下面函式。當我們螢幕剛要點亮,這個時候mScreenBrightness為0,所以這個時候呼叫mPhotonicModulator.setState設定state是點亮,但是brightness是0的。
private final Runnable mScreenUpdateRunnable = new Runnable() { @Override public void run() { mScreenUpdatePending = false; int brightness = mScreenState != Display.STATE_OFF && mColorFadeLevel > 0f ? mScreenBrightness : 0; if (mPhotonicModulator.setState(mScreenState, brightness)) { if (DEBUG) { Slog.d(TAG, "Screen ready"); } mScreenReady = true; invokeCleanListenerIfNeeded(); } else { if (DEBUG) { Slog.d(TAG, "Screen not ready"); } } } };
DisplayPowerState的設定亮度狀態邏輯分析
mPhotonicModulator.setState應該要PhotonicModulator的run函式結合一起看。
public boolean setState(int state, int backlight) { synchronized (mLock) { boolean stateChanged = state != mPendingState; boolean backlightChanged = backlight != mPendingBacklight; if (stateChanged || backlightChanged) { if (DEBUG) { Slog.d(TAG, "Requesting new screen state: state=" + Display.stateToString(state) + ", backlight=" + backlight); } mPendingState = state; mPendingBacklight = backlight; boolean changeInProgress = mStateChangeInProgress || mBacklightChangeInProgress; mStateChangeInProgress = stateChanged; mBacklightChangeInProgress = backlightChanged; if (!changeInProgress) { Slog.d(TAG,"notify set backlight thread run"); mLock.notifyAll(); } } return !mStateChangeInProgress; } }
兩者結合看先setState設定了狀態,只有狀態改變時,我們才能重新設定狀態(設定到mpendingState和mPendingBacklight)。而在run函式中,當設定的狀態mPendingState、mPendingBacklight和mActualState、mActualBacklight(真正設定到背光的狀態、亮度)不一樣時,才會呼叫mBlanker.requestDisplayState設定亮度。否則狀態沒有改變,就會把mStateChangeInProgress 和mBacklightChangeInProgress 設定為false,然後執行緒就wait住。
而此時setState重新設定下來的話,這個時候把亮度和狀態設定到mPendingState 和mPendingBacklight 。然後這時mStateChangeInProgress 和 mBacklightChangeInProgress都是false。這樣就可以呼叫mLock的notifyAll函式重新喚醒執行緒,這樣就把把前面setState設定下來的mPendingState和mPendingBacklight再通過mBlanker.requestDisplayState設定到背光裝置中去。
@Override
public void run() {
for (;;) {
// Get pending change.
final int state;
final boolean stateChanged;
final int backlight;
final boolean backlightChanged;
synchronized (mLock) {
state = mPendingState;
stateChanged = (state != mActualState);
backlight = mPendingBacklight;
backlightChanged = (backlight != mActualBacklight);
if (!stateChanged) {
// State changed applied, notify outer class.
postScreenUpdateThreadSafe();
mStateChangeInProgress = false;
}
if (!backlightChanged) {
mBacklightChangeInProgress = false;
}
if (!stateChanged && !backlightChanged) {
try {
mLock.wait();
} catch (InterruptedException ex) { }
continue;
}
mActualState = state;
mActualBacklight = backlight;
}
// Apply pending change.
if (true) {
Slog.d(TAG, "Updating screen state: state="
+ Display.stateToString(state) + ", backlight=" + backlight);
}
mBlanker.requestDisplayState(state, backlight);
Slog.d(TAG, "kangchen Updating screen state: state=");
}
}
設定亮度、狀態到背光裝置
DisplayBlanker的requestDisplayState如下,主要呼叫requestGlobalDisplayStateInternal函式。
DisplayBlanker blanker = new DisplayBlanker() {
@Override
public void requestDisplayState(int state, int brightness) {
// The order of operations is important for legacy reasons.
if (state == Display.STATE_OFF) {
requestGlobalDisplayStateInternal(state, brightness);
}
callbacks.onDisplayStateChange(state);
if (state != Display.STATE_OFF) {
requestGlobalDisplayStateInternal(state, brightness);
}
}
};
requestGlobalDisplayStateInternal函式先是對state和brightness的處理,然後把這個兩個變數放在mGlobalDisplayState 和mGlobalDisplayBrightness成員變數中。緊接著呼叫applyGlobalDisplayStateLocked函式mTempDisplayStateWorkQueue作為引數。最後再呼叫mTempDisplayStateWorkQueue各個成員的run函式(這裡返回的是Runnable介面,這裡就會設定狀態和亮度到裝置中去)。
private void requestGlobalDisplayStateInternal(int state, int brightness) {
if (state == Display.STATE_UNKNOWN) {
state = Display.STATE_ON;
}
if (state == Display.STATE_OFF) {
brightness = PowerManager.BRIGHTNESS_OFF;
} else if (brightness < 0) {
brightness = PowerManager.BRIGHTNESS_DEFAULT;
} else if (brightness > PowerManager.BRIGHTNESS_ON) {
brightness = PowerManager.BRIGHTNESS_ON;
}
synchronized (mTempDisplayStateWorkQueue) {
try {
synchronized (mSyncRoot) {
if (mGlobalDisplayState == state
&& mGlobalDisplayBrightness == brightness) {
return; // no change
}
mGlobalDisplayState = state;
mGlobalDisplayBrightness = brightness;
applyGlobalDisplayStateLocked(mTempDisplayStateWorkQueue);
}
// 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.
for (int i = 0; i < mTempDisplayStateWorkQueue.size(); i++) {
mTempDisplayStateWorkQueue.get(i).run();//設定亮度、狀態到裝置
}
} finally {
mTempDisplayStateWorkQueue.clear();
}
}
}
applyGlobalDisplayStateLocked函式會遍歷各個顯示裝置(多顯示),然後呼叫updateDisplayStateLocked函式返回一個Runnable,最後把這個Runnable放入之前傳入的mTempDisplayStateWorkQueue佇列中。
private void applyGlobalDisplayStateLocked(List<Runnable> workQueue) {
final int count = mDisplayDevices.size();
for (int i = 0; i < count; i++) {
DisplayDevice device = mDisplayDevices.get(i);
Runnable runnable = updateDisplayStateLocked(device);
if (runnable != null) {
workQueue.add(runnable);
}
}
}
那下面我們看下updateDisplayStateLocked函式,主要是呼叫了DisplayDevice的requestDisplayStateLocked函式,當然mGlobalDisplayState和mGlobalDisplayBrightness作為引數。
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) {
return device.requestDisplayStateLocked(mGlobalDisplayState, mGlobalDisplayBrightness);
}
return null;
}
這裡的DisplayDevice的requestDisplayStateLocked函式,是在LocalDisplayAdapter中實現的,這裡吧state和brightness儲存在mState和mBrightness中,然後返回Runnable介面,最後在Runnable介面中設定亮度和狀態。
public Runnable requestDisplayStateLocked(final int state, final int brightness) {
// Assume that the brightness is off if the display is being turned off.
assert state != Display.STATE_OFF || brightness == PowerManager.BRIGHTNESS_OFF;
final boolean stateChanged = (mState != state);
final boolean brightnessChanged = (mBrightness != brightness) && mBacklight != null;
if (stateChanged || brightnessChanged) {
final int displayId = mBuiltInDisplayId;
final IBinder token = getDisplayTokenLocked();
final int oldState = mState;
if (stateChanged) {
mState = state;//儲存state
updateDeviceInfoLocked();
}
if (brightnessChanged) {
mBrightness = brightness;//儲存brightness
}
// 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() {//返回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 {
return; // old state and new state is off
}
}
// Apply brightness changes given that we are in a non-suspended state.
if (brightnessChanged) {
Slog.d(TAG, "setDisplayBrightnessbrightness1=" + brightness);
setDisplayBrightness(brightness);
Slog.d(TAG, "setDisplayBrightnessbrightness2=" + brightness);
}
// Enter the final desired state, possibly suspended.
if (state != currentState) {
setDisplayState(state);
}
}
private void setDisplayState(int state) {
if (DEBUG) {
Slog.d(TAG, "setDisplayState("
+ "id=" + displayId
+ ", state=" + Display.stateToString(state) + ")");
}
try {
final int mode = getPowerModeForState(state);
SurfaceControl.setDisplayPowerMode(token, mode);//到SurfaceControl設定狀態
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
private void setDisplayBrightness(int brightness) {
try {
mBacklight.setBrightness(brightness);//設定亮度
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
};
}
return null;
}
DisplayPowerControl設定亮度邏輯(根據VSync訊號將亮度慢慢變亮)
上面在DisplayPowerState中僅僅是設定狀態,比如剛點亮螢幕這個時候其實設定的brightness為0,我們繼續分析DisplayPowerState的updatePowerState函式。在updatePowerState函式中,當設定亮度時會呼叫如下程式碼:
if (!mPendingScreenOff) {
if (state == Display.STATE_ON || state == Display.STATE_DOZE) {
animateScreenBrightness(brightness,
slowChange ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST);
} else {
animateScreenBrightness(brightness, 0);
}
}
我們注意到這裡有一個BRIGHTNESS_RAMP_RATE_SLOW 和BRIGHTNESS_RAMP_RATE_FAST(這裡涉及到亮度顯示原理我們後面分析),先看animateScreenBrightness函式。這裡函式主要呼叫了mScreenBrightnessRampAnimator.animateTo函式。
private void animateScreenBrightness(int target, int rate) {
if (mScreenBrightnessRampAnimator.animateTo(target, rate)) {
try {
mBatteryStats.noteScreenBrightness(target);
} catch (RemoteException ex) {
// same process
}
}
}
我們再來看mScreenBrightnessRampAnimator 物件的建立
mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>(
mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS);
我們注意一個引數是DisplayPowerState物件mPowerState,另一個引數是DisplayPowerState.SCREEN_BRIGHTNESS
public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS =
new IntProperty<DisplayPowerState>("screenBrightness") {
@Override
public void setValue(DisplayPowerState object, int value) {
object.setScreenBrightness(value);
}
@Override
public Integer get(DisplayPowerState object) {
return object.getScreenBrightness();
}
};
RampAnimator的建構函式。
public RampAnimator(T object, IntProperty<T> property) {
mObject = object;
mProperty = property;
mChoreographer = Choreographer.getInstance();
}
我們結合RampAnimator的建構函式,再來分析RampAnimator的animateTo函式。
1. 當rate<=0時,這個時候,我們直接呼叫mProperty.setValue。就是呼叫DisplayPowerState的setScreenBrightness函式。這個setScreenBrightness函式我們後面分析。
2. 當rate>0時,這個時候會呼叫postAnimationCallback函式(這個函式根據VSync訊號過來,把亮度慢慢上升的一個過程),而且mAnimating置為true。
public boolean animateTo(int target, int rate) {
// Immediately jump to the target the first time.
if (mFirstTime || rate <= 0) {
if (mFirstTime || target != mCurrentValue) {
mFirstTime = false;
mRate = 0;
mTargetValue = target;
mCurrentValue = target;
mProperty.setValue(mObject, target);//設定值
if (mAnimating) {
mAnimating = false;
cancelAnimationCallback();
}
if (mListener != null) {
mListener.onAnimationEnd();
}
return true;
}
return false;
}
// 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();
postAnimationCallback();
}
return changed;
}
下面我們先分析postAnimationCallback函式,這個和之前分析WMS的VSync訊號類似,當VSync訊號過來時,會呼叫mAnimationCallback函式。(可以看之前部落格http://blog.csdn.net/kc58236582/article/details/53835998)
private void postAnimationCallback() {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
}
那我們繼續看mAnimationCallback 的run函式,這個函式噹噹前值和上一次值不一樣,我們就呼叫DisplayPowerState的setScreenBrightness來設定亮度。而且當前值不是目標值,我們就繼續呼叫postAnimationCallback函式,來設定VSync回撥。最後當亮度變成目標值後,將mAnimating 置為false,代表亮度變化的動畫結束了。
private final Runnable mAnimationCallback = new Runnable() {
@Override // Choreographer callback
public void run() {
final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos)
* 0.000000001f;
mLastFrameTimeNanos = frameTimeNanos;
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 int oldCurrentValue = mCurrentValue;
mCurrentValue = Math.round(mAnimatedValue);
if (oldCurrentValue != mCurrentValue) {
mProperty.setValue(mObject, mCurrentValue);//設定到DisplayPowerState的setScreenBrightness函式中
}
if (mTargetValue != mCurrentValue) {//當前值還不是目標值,繼續設定VSync回撥
postAnimationCallback();
} else {
mAnimating = false;//否則動畫標誌置為false
if (mListener != null) {
mListener.onAnimationEnd();
}
}
}
}
最後我們再看下DisplayPowerState的SetScreenBrightness函式,將亮度設定到mScreenBrightness 中,當螢幕狀態不為off時,呼叫scheduleScreenUpdate函式(所以肯定先要呼叫setScreenState來設定螢幕狀態為on,這樣才能設定螢幕亮度)。
public void setScreenBrightness(int brightness) {
if (mScreenBrightness != brightness) {
mScreenBrightness = brightness;
if (mScreenState != Display.STATE_OFF) {
mScreenReady = false;
scheduleScreenUpdate();
}
}
}
scheduleScreenUpdate最後通過發訊息會呼叫如下程式碼,這裡狀態已經是ON了,就會把剛剛設定的mScreenBrightness作為引數設定到mPhotonicModulator.setState中。流程和設定state一樣了,只是一開始亮屏時,設定state時,亮度為0而已。 private final Runnable mScreenUpdateRunnable = new Runnable() {
@Override
public void run() {
mScreenUpdatePending = false;
int brightness = mScreenState != Display.STATE_OFF
&& mColorFadeLevel > 0f ? mScreenBrightness : 0;
if (mPhotonicModulator.setState(mScreenState, brightness)) {
if (DEBUG) {
Slog.d(TAG, "Screen ready");
}
mScreenReady = true;
invokeCleanListenerIfNeeded();
} else {
if (DEBUG) {
Slog.d(TAG, "Screen not ready");
}
}
}
};