android 6.0 亮屏灭屏流程分析

本文深入剖析安卓系统中屏幕亮屏与灭屏的工作流程,从PowerManagerService到DisplayPowerControl,再到PhoneWindowManager和WindowManagerService,详细解读各模块如何协作完成屏幕状态的切换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

亮屏、灭屏流程整个流程涉及的模块比较多,包括PowerManagerService、DisplayPowerControl、WMS、AMS。因此在分析完WMS之后,我们把这块也分析下。


DisplayPowerControl

我们知道灭屏流程的发起是在PowerManagerService中,会通过updatePowerState函数调用updateDisplayPowerStateLocked函数,再调用DisplayPowerControl的requestPowerState函数,到DisplayPowerControl中。DisplayPowerControl中后面会调用updatePowerState函数,我们也主要从这个函数开始分析:

updatePowerState会根据PowerManagerService传过来的显示状态,然后调用animateScreenStateChange函数。

        animateScreenStateChange(state, performScreenOffTransition);

下面我们先来看animateScreenStateChange函数:

        private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
            // If there is already an animation in progress, don't interfere with it.
            if (mColorFadeOnAnimator.isStarted()
                    || mColorFadeOffAnimator.isStarted()) {
                return;
            }
     
            // If we were in the process of turning off the screen but didn't quite
            // finish.  Then finish up now to prevent a jarring transition back
            // to screen on if we skipped blocking screen on as usual.
            if (mPendingScreenOff && target != Display.STATE_OFF) {
                setScreenState(Display.STATE_OFF);
                mPendingScreenOff = false;
                mPowerState.dismissColorFadeResources();
            }
     
            if (target == Display.STATE_ON) {//亮屏处理
                // Want screen on.  The contents of the screen may not yet
                // be visible if the color fade has not been dismissed because
                // its last frame of animation is solid black.
                if (!setScreenState(Display.STATE_ON)) {
                    return; // screen on blocked
                }
                if (USE_COLOR_FADE_ON_ANIMATION && mPowerRequest.isBrightOrDim()) {//亮屏动画
                    // Perform screen on animation.
                    if (mPowerState.getColorFadeLevel() == 1.0f) {
                        mPowerState.dismissColorFade();
                    } else if (mPowerState.prepareColorFade(mContext,
                            mColorFadeFadesConfig ?
                                    ColorFade.MODE_FADE :
                                            ColorFade.MODE_WARM_UP)) {
                        mColorFadeOnAnimator.start();
                    } else {
                        mColorFadeOnAnimator.end();
                    }
                } else {//跳过亮屏动画
                    // Skip screen on animation.
                    mPowerState.setColorFadeLevel(1.0f);
                    mPowerState.dismissColorFade();
                }
            } else if (target == Display.STATE_DOZE) {
                // Want screen dozing.
                // Wait for brightness animation to complete beforehand when entering doze
                // from screen on to prevent a perceptible jump because brightness may operate
                // differently when the display is configured for dozing.
            ......
            } else if (target == Display.STATE_DOZE_SUSPEND) {
                // Want screen dozing and suspended.
                // Wait for brightness animation to complete beforehand unless already
                // suspended because we may not be able to change it after suspension.
            ......
            } else {//灭屏处理
                // Want screen off.
                mPendingScreenOff = true;
                if (mPowerState.getColorFadeLevel() == 0.0f) {//灭屏动画结束
                    // Turn the screen off.
                    // A black surface is already hiding the contents of the screen.
                    setScreenState(Display.STATE_OFF);
                    mPendingScreenOff = false;
                    mPowerState.dismissColorFadeResources();
                } else if (performScreenOffTransition
                        && mPowerState.prepareColorFade(mContext,
                                mColorFadeFadesConfig ?
                                        ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)
                        && mPowerState.getScreenState() != Display.STATE_OFF) {
                    // Perform the screen off animation.
                    mColorFadeOffAnimator.start();//开启灭屏动画
                } else {
                    // Skip the screen off animation and add a black surface to hide the
                    // contents of the screen.
                    mColorFadeOffAnimator.end();//关闭灭屏动画
                }
            }
        }

animateScreenStateChange在亮屏的处理的时候,先会调用setScreenState(Display.STATE_ON),然后根据USE_COLOR_FADE_ON_ANIMATION 判断是否要开启亮屏动画,这里我们是没有设置的。因此直接跳过亮屏动画。灭屏的处理的话,会有一个灭屏动画(也是注册一个VSync信号回调函数处理的,这里我们不分析了),当动画结束后,直接就调用setScreenState(Display.STATE_OFF)结束。

我们再来看看setScreenState函数

        private boolean setScreenState(int state) {
            if (mPowerState.getScreenState() != state) {
                final boolean wasOn = (mPowerState.getScreenState() != Display.STATE_OFF);
                mPowerState.setScreenState(state);
            ......
            }
     
            // Tell the window manager policy when the screen is turned off or on unless it's due
            // to the proximity sensor.  We temporarily block turning the screen on until the
            // window manager is ready by leaving a black surface covering the screen.
            // This surface is essentially the final state of the color fade animation and
            // it is only removed once the window manager tells us that the activity has
            // finished drawing underneath.
            final boolean isOff = (state == Display.STATE_OFF);
            if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF
                    && !mScreenOffBecauseOfProximity) {
                mReportedScreenStateToPolicy = REPORTED_TO_POLICY_SCREEN_OFF;
                unblockScreenOn();
                mWindowManagerPolicy.screenTurnedOff();//调用PhoneWindowManager的screenTurnedOff
            } else if (!isOff && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF) {
                mReportedScreenStateToPolicy = REPORTED_TO_POLICY_SCREEN_TURNING_ON;
                if (mPowerState.getColorFadeLevel() == 0.0f) {
                    blockScreenOn();
                } else {
                    unblockScreenOn();
                }
                mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker);//调用PhoneWindowManager的screenTurningOn函数
            }
     
            // Return true if the screen isn't blocked.
            return mPendingScreenOnUnblocker == null;
        }

setScreenState函数,先是调用了DisplayPowerState的setScreenState函数,然后根据屏幕是灭屏还是亮屏调用PhoneWindowManager的相关函数。


PhoneWindowManager的screenTurnedOff和screenTurningOn函数

PhoneWindowManager的screenTurnedOff函数主要是通知kerguard,屏幕灭屏了。

        @Override
        public void screenTurnedOff() {
            if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turned off...");
     
            updateScreenOffSleepToken(true);
            synchronized (mLock) {
                mScreenOnEarly = false;
                mScreenOnFully = false;
                mKeyguardDrawComplete = false;
                mWindowManagerDrawComplete = false;
                mScreenOnListener = null;
                updateOrientationListenerLp();
     
                if (mKeyguardDelegate != null) {
                    mKeyguardDelegate.onScreenTurnedOff();
                }
            }
        }

我们再来看PhoneWindowManager的screenTurningOn函数。当有keyguard时,我们会先发一个延时的MSG_KEYGUARD_DRAWN_TIMEOUT信号,并且会调用keyguard的onScreenTurningOn函数,当完成会调用mKeyguardDrawnCallback回调函数。我们这里还要注意下有一个屏幕点亮后的回调。

        @Override
        public void screenTurningOn(final ScreenOnListener screenOnListener) {
            if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on...");
     
            updateScreenOffSleepToken(false);
            synchronized (mLock) {
                mScreenOnEarly = true;
                mScreenOnFully = false;
                mKeyguardDrawComplete = false;
                mWindowManagerDrawComplete = false;
                mScreenOnListener = screenOnListener;//屏幕点亮后的回调
     
                if (mKeyguardDelegate != null) {
                    if (DEBUG_WAKEUP) Slog.d(TAG,
                            "send delay message MSG_KEYGUARD_DRAWN_TIMEOUT");
                    mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
                    mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, 1000);
                    mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);
                } else {
                    if (DEBUG_WAKEUP) Slog.d(TAG,
                            "null mKeyguardDelegate: setting mKeyguardDrawComplete.");
                    finishKeyguardDrawn();
                }
            }
        }

我们先看下mKeyguardDrawnCallback 回调,就是发送MSG_KEYGUARD_DRAWN_COMPLETE(keyguard绘制完的消息)

        final DrawnListener mKeyguardDrawnCallback = new DrawnListener() {
            @Override
            public void onDrawn() {
                if (DEBUG_WAKEUP) Slog.d(TAG, "mKeyguardDelegate.ShowListener.onDrawn.");
                mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
            }
        };

我们再来看看MSG_KEYGUARD_DRAWN_COMPLETE以及MSG_KEYGUARD_DRAWN_TIMEOUT信号的处理,都会调用finishKeyguardDrawn函数。

                    case MSG_KEYGUARD_DRAWN_COMPLETE:
                        if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mKeyguardDrawComplete");
                        finishKeyguardDrawn();
                        break;
                    case MSG_KEYGUARD_DRAWN_TIMEOUT:
                        Slog.w(TAG, "Keyguard drawn timeout. Setting mKeyguardDrawComplete");
                        finishKeyguardDrawn();
                        break;

我们再来看看finishKeyguardDrawn函数,会先去除队列中的MSG_KEYGUARD_DRAWN_TIMEOUT消息(因为之前发的MSG_KEYGUARD_DRAWN_TIMEOUT消息,可能keyguard结束发送MSG_KEYGUARD_DRAWN_COMPLETE消息调用的finishKeyguardDrawn就要把MSG_KEYGUARD_DRAWN_TIMEOUT去除了)。然后会调用

        private void finishKeyguardDrawn() {
            synchronized (mLock) {
                if (!mScreenOnEarly || mKeyguardDrawComplete) {
                    return; // We are not awake yet or we have already informed of this event.
                }
     
                mKeyguardDrawComplete = true;
                if (mKeyguardDelegate != null) {
                    mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
                }
                mWindowManagerDrawComplete = false;
            }
     
            // ... eventually calls finishWindowsDrawn which will finalize our screen turn on
            // as well as enabling the orientation change logic/sensor.
            mWindowManagerInternal.waitForAllWindowsDrawn(mWindowManagerDrawCallback,
                    WAITING_FOR_DRAWN_TIMEOUT);
        }

最后我们再看看WMS的waitForAllWindowsDrawn函数,以及两个参数mWindowManagerDrawCallback和一个WAITING_FOR_DRAWN_TIMEOUT(1秒)。


WMS的waitForAllWindowsDrawn函数

我们先来看看WMS的waitForAllWindowsDrawn函数,会把传进来的回调保存在mWaitingForDrawnCallback 。然后遍历所有的windows,把需要显示或者已经显示的窗口全部加入到mWaitingForDrawn,然后调用requestTraversalLocked这个函数我们之前分析过,就是发送一个消息,重新刷新UI布局。然后我们继续分析这个函数,如果mWaitingForDrawn为空,代表没啥显示的直接调用回调函数,如果mWaitingForDrawn有要显示的窗口,就要会先发送一个WAITING_FOR_DRAWN_TIMEOUT,这个timeout之前传进来的是1秒。然后调用checkDrawnWindowsLocked函数。

            @Override
            public void waitForAllWindowsDrawn(Runnable callback, long timeout) {
                synchronized (mWindowMap) {
                    mWaitingForDrawnCallback = callback;//回调保存在mWaitingForDrawnCallback
                    final WindowList windows = getDefaultWindowListLocked();
                    for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
                        final WindowState win = windows.get(winNdx);
                        final boolean isForceHiding = mPolicy.isForceHiding(win.mAttrs);
                        Slog.i(TAG,"In the function waitForAllWindowsDrawn");
                        if (win.isVisibleLw()
                                && (win.mAppToken != null || isForceHiding)) {
                            Slog.i(TAG,"In the function win.isVisibleLw()");
                            win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
                            // Force add to mResizingWindows.
                            win.mLastContentInsets.set(-1, -1, -1, -1);
                            mWaitingForDrawn.add(win);
     
                            // No need to wait for the windows below Keyguard.
                            if (isForceHiding) {
                                break;
                            }
                        }
                    }
                    requestTraversalLocked();
                }
                mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
                if (mWaitingForDrawn.isEmpty()) {
                    callback.run();
                    Slog.i(TAG,"In the function mWaitingForDrawn.isEmpty()");
                } else {
                    mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, timeout);
                    checkDrawnWindowsLocked();
                    Slog.i(TAG,"In the function checkDrawnWindowsLocked()");
                }
            }

我们先来看下checkDrawnWindowsLocked函数,这个函数。遍历之前加入的mWaitingForDrawn(要显示的窗口),这个时候我们把已经去除的,不需要显示的,没有surface的窗口从mWaitingForDrawn去除,还有已经绘制好的也去除。然后再当mWaitingForDrawn为空时,就发送ALL_WINDOWS_DRAWN消息。

        void checkDrawnWindowsLocked() {
            if (mWaitingForDrawn.isEmpty() || mWaitingForDrawnCallback == null) {
                return;
            }
            for (int j = mWaitingForDrawn.size() - 1; j >= 0; j--) {
                WindowState win = mWaitingForDrawn.get(j);
                if (win.mRemoved || !win.mHasSurface || !win.mPolicyVisibility) {
                    // Window has been removed or hidden; no draw will now happen, so stop waiting.
                    if (DEBUG_SCREEN_ON) Slog.w(TAG, "Aborted waiting for drawn: " + win);
                    mWaitingForDrawn.remove(win);
                } else if (win.hasDrawnLw()) {
                    // Window is now drawn (and shown).
                    if (DEBUG_SCREEN_ON) Slog.d(TAG, "Window drawn win=" + win);
                    mWaitingForDrawn.remove(win);
                }
            }
            if (mWaitingForDrawn.isEmpty()) {
                if (DEBUG_SCREEN_ON) Slog.d(TAG, "All windows drawn!");
                mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
                mH.sendEmptyMessage(H.ALL_WINDOWS_DRAWN);
            }
        }

ALL_WINDOWS_DRAWN消息的处理就是清除mWaitingForDrawnCallback ,然后调用回调。

                    case ALL_WINDOWS_DRAWN: {
                        Runnable callback;
                        synchronized (mWindowMap) {
                            callback = mWaitingForDrawnCallback;
                            mWaitingForDrawnCallback = null;
                        }
                        if (callback != null) {
                            callback.run();
                        }
                    }

还有当我们调用waitForAllWindowsDrawn一般都是有需要显示的窗口,但是我们直接调用checkDrawnWindowsLocked函数,发现有的窗口还没绘制完成。那么我们就要等,会在刷新的核心函数中performLayoutAndPlaceSurfacesLockedInner有如下代码,这个时候如果之前还没绘制完成的窗口,绘制好了。会再调用checkDrawnWindowsLocked函数,如果mWaitingForDrawn中的窗口绘制好了,会在mWaitingForDrawn中去除这个窗口。然后mWaitingForDrawn为空了,之后会发送ALL_WINDOWS_DRAWN消息,还调用mWaitingForDrawnCallback回调函数。

            if (mWaitingForDrawnCallback != null ||
                    (mInnerFields.mOrientationChangeComplete && !defaultDisplay.layoutNeeded &&
                            !mInnerFields.mUpdateRotation)) {
                checkDrawnWindowsLocked();
            }

当然如果我们之前没有把mWaitingForDrawn中的窗口清空,最后在WAITING_FOR_DRAWN_TIMEOUT(这里是1秒)时间到了也会调用回调的。

                    case WAITING_FOR_DRAWN_TIMEOUT: {
                        Runnable callback = null;
                        synchronized (mWindowMap) {
                            Slog.w(TAG, "Timeout waiting for drawn: undrawn=" + mWaitingForDrawn);
                            mWaitingForDrawn.clear();
                            callback = mWaitingForDrawnCallback;
                            mWaitingForDrawnCallback = null;
                        }
                        if (callback != null) {
                            callback.run();
                        }
                        break;
                    }

 

PhoneWindowManager窗口绘制完成的回调函数

那下面我们就要继续看PhoneWindowManager中窗口绘制完成之后的回调函数。代码如下就是发送了一个消息。

        final Runnable mWindowManagerDrawCallback = new Runnable() {
            @Override
            public void run() {
                if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for display!");
                mHandler.sendEmptyMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE);
            }
        };

我们来看这个消息的处理

                    case MSG_WINDOW_MANAGER_DRAWN_COMPLETE:
                        if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mWindowManagerDrawComplete");
                        finishWindowsDrawn();
                        break;

finishWindowsDrawn就是把mWindowManagerDrawComplete 置为true,然后调用finishScreenTurningOn函数。

        private void finishWindowsDrawn() {
            synchronized (mLock) {
                if (!mScreenOnEarly || mWindowManagerDrawComplete) {
                    return; // Screen is not turned on or we did already handle this case earlier.
                }
     
                mWindowManagerDrawComplete = true;
            }
     
            finishScreenTurningOn();
        }

finishScreenTurningOn函数调用了之前在DisplayPowerControl中调用screenTurningOn传入的回调,然后再调用WMS的enableScreenIfNeeded函数。

        private void finishScreenTurningOn() {
            synchronized (mLock) {
                updateOrientationListenerLp();
            }
            final ScreenOnListener listener;
            final boolean enableScreen;
            synchronized (mLock) {
     
                if (mScreenOnFully || !mScreenOnEarly || !mWindowManagerDrawComplete
                        || (mAwake && !mKeyguardDrawComplete)) {
                    return; // spurious or not ready yet
                }
     
                listener = mScreenOnListener;
                mScreenOnListener = null;
                mScreenOnFully = true;
                ......
            }
     
            if (listener != null) {
                listener.onScreenOn();
            }
     
            if (enableScreen) {
                try {
                    mWindowManager.enableScreenIfNeeded();
                } catch (RemoteException unhandled) {
                }
            }
        }

我们先分析下WMS的enableScreenIfNeeded函数,然后再看DisplayPowerControl的回调onScreenOn函数。

 

WMS的enableScreenIfNeeded函数

WMS的enableScreenIfNeeded函数就是调用了enableScreenIfNeededLocked函数

        @Override
        public void enableScreenIfNeeded() {
            synchronized (mWindowMap) {
                enableScreenIfNeededLocked();
            }
        }

enableScreenIfNeededLocked这个函数仅仅是保证mDisplayEnabled为true,如果为true直接结束。

        void enableScreenIfNeededLocked() {
            if (mDisplayEnabled) {
                return;
            }
            if (!mSystemBooted && !mShowingBootMessages) {
                return;
            }
            mH.sendEmptyMessage(H.ENABLE_SCREEN);
        }

mDisplayEnabled不为true,发送ENABLE_SCREEN消息

                    case ENABLE_SCREEN: {
                        performEnableScreen();
                        break;

performEnableScreen函数会让SurfaceFlinger去停止开机动画等,也会把mDisplayEnabled置为true。当然performEnableScreen在开机的时候会AMS中调用WMS的enableScreenAfterBoot函数来调用performEnableScreen函数。这个我们在博客http://blog.youkuaiyun.com/kc58236582/article/details/52921978分析过了。

        public void performEnableScreen() {
            synchronized(mWindowMap) {
                if (mDisplayEnabled) {
                    return;
                }
                if (!mSystemBooted && !mShowingBootMessages) {
                    return;
                }
     
                // Don't enable the screen until all existing windows have been drawn.
                if (!mForceDisplayEnabled && checkWaitingForWindowsLocked()) {
                    return;
                }
     
                if (!mBootAnimationStopped) {
                    // Do this one time.
                    try {//停止开机动画
                        IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
                        if (surfaceFlinger != null) {
                            //Slog.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
                            Parcel data = Parcel.obtain();
                            data.writeInterfaceToken("android.ui.ISurfaceComposer");
                            surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
                                    data, null, 0);
                            data.recycle();
                        }
                    } catch (RemoteException ex) {
                        Slog.e(TAG, "Boot completed: SurfaceFlinger is dead!");
                    }
                    mBootAnimationStopped = true;
                }
     
                if (!mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) {
                    if (DEBUG_BOOT) Slog.i(TAG, "performEnableScreen: Waiting for anim complete");
                    return;
                }
     
                mDisplayEnabled = true;//置为true
                if (DEBUG_SCREEN_ON || DEBUG_BOOT) Slog.i(TAG, "******************** ENABLING SCREEN!");
     
                // Enable input dispatch.
                mInputMonitor.setEventDispatchingLw(mEventDispatchingEnabled);
            }
     
            try {
                mActivityManager.bootAnimationComplete();
            } catch (RemoteException e) {
            }
     
            mPolicy.enableScreenAfterBoot();
     
            // Make sure the last requested orientation has been applied.
            updateRotationUnchecked(false, false);
        }


窗口绘制完成后调用DisplayPowerControl中的回调

当WMS窗口绘制完成后,会在PhoneWindowManager中的finishScreenTurningOn函数调用DisplayPowerControl的回调函数。最后我们再来看看DisplayPowerControl中的回调的onScreenOn函数。只是发送了一个MSG_SCREEN_ON_UNBLOCKED消息。

        private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener {
            @Override
            public void onScreenOn() {
                Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this);
                msg.setAsynchronous(true);
                mHandler.sendMessage(msg);
            }
        }

MSG_SCREEN_ON_UNBLOCKED的处理先是调用了unblockScreenOn函数,然后再调用updatePowerState更新状态。

                    case MSG_SCREEN_ON_UNBLOCKED:
                        if (mPendingScreenOnUnblocker == msg.obj) {
                            unblockScreenOn();
                            updatePowerState();
                        }
                        break;

unblockScreenOn函数,只是打印下从调用screenTurningOn开始,到窗口绘制完成在PhoneWindowManager中回调这个函数的时间差打印。

        private void unblockScreenOn() {
            if (mPendingScreenOnUnblocker != null) {
                mPendingScreenOnUnblocker = null;
                long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime;
                Slog.i(TAG, "Unblocked screen on after " + delay + " ms");
                Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
            }
        }


 
---------------------  
作者:kc专栏  
来源:优快云  
原文:https://blog.youkuaiyun.com/kc58236582/article/details/54612777  
版权声明:本文为博主原创文章,转载请附上博文链接!

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值