1. 程式人生 > >Android6.0 亮屏滅屏流程(DisplayPowerControler、WMS)(二)亮度設定

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");
                }
            }
        }
    };