亮屏流程

亮屏有很多种方式,如Power键亮屏、插拔USB亮屏、来电亮屏......,虽然方式不同,但只要发起亮屏,其流程都是一样的。PowerManager中提供了wakeUp()方法给其他组件或应用来点亮屏幕,下面就从这个方法开始分析亮屏流程。/**

* @param time  亮屏时间

* @param reason 亮屏原因

* @param details 细节描述

*/

public void wakeUp(long time, @WakeReason int reason, String details) {

    try {

        mService.wakeUp(time, reason, details, mContext.getOpPackageName());

    } catch (RemoteException e) {

        throw e.rethrowFromSystemServer();

    }

}

调用PowerManager的wakeUp()后,将直接调用PMS#wakeUp()方法,在进行权限检查后,调用wakeUpInternal()进入到PMS内部流程:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void wakeUpInternal(long eventTime, @WakeReason int reason, String details, int uid,

        String opPackageName, int opUid) {

    synchronized (mLock) {

        // 进行亮屏流程

        if (wakeUpNoUpdateLocked(eventTime, reason, details, uid, opPackageName, opUid)) {

            // 更新全部状态

            updatePowerStateLocked();

        }

    }

}

这个方法中:

首先,调用wakeUpNoUpdateLocked()方法;

然后,根据调用返回值确定是否更新全局状态;

1.wakeUpNoUpdateLocked()验证和亮屏状态更新

此方法是进行亮屏的主要方法,如果亮屏成功,该方法返回true,并更新全局状态,如果因不满足亮屏条件,则返回false,说明亮屏失败:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private boolean wakeUpNoUpdateLocked(long eventTime, @WakeReason int reason, String details,

        int reasonUid, String opPackageName, int opUid) {

    // 此次亮屏时间小于最近一次灭屏时间、已经处于亮屏、系统未启动完成或强制suspend,不会进行亮屏

    if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE

            || !mBootCompleted || !mSystemReady || mForceSuspendActive) {

        return false;

    }

    try {

        mLastWakeTime = eventTime;  // 更新最后一次亮屏时间

        mLastWakeReason = reason;  // 更新亮屏原因

        // 更新mWakefulness值

        setWakefulnessLocked(WAKEFULNESS_AWAKE, reason, eventTime);

        // 通知其他组件亮屏动作

        mNotifier.onWakeUp(reason, details, reasonUid, opPackageName, opUid);

        // 更新用户活动时间

        userActivityNoUpdateLocked(

                eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);

    } finally {

        Trace.traceEnd(Trace.TRACE_TAG_POWER);

    }

    return true;

}

首先,进行亮屏时间和状态的验证,如果满足以下三个条件之一,则亮屏不会成功,返回false,验证完成后,开始执行亮屏流程:

此次亮屏时间小于最近一次灭屏时间;

唤醒状态已经处于亮屏;

系统未启动完成或强制suspend。

接下来,会更新mLastWakeTime和mLastWakeReason,表示最后一次亮屏时间和原因。

然后,调用setWakefulnessLocked()方法来设置表示PMS唤醒状态mWakefulness的值。

最后,执行userActivityNoUpdateLocked()方法更新用户活动时间。

1.1.setWakefulnessLocked()更新唤醒状态

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

void setWakefulnessLocked(int wakefulness, int reason, long eventTime) {

    if (getWakefulnessLocked() != wakefulness) {

        // 更新mWakefulness

        mWakefulnessRaw = wakefulness;

        mWakefulnessChanging = true;

        mDirty |= DIRTY_WAKEFULNESS;

        mDozeStartInProgress &= (getWakefulnessLocked() == WAKEFULNESS_DOZING);

        // mNotifier中做mWakefuless转变的开始工作

        if (mNotifier != null) {

            mNotifier.onWakefulnessChangeStarted(wakefulness, reason, eventTime);

        }

        // 通知mAttentionDetector系统状态的改变

        mAttentionDetector.onWakefulnessChangeStarted(wakefulness);

    }

}

这里会将mWakefulnessRaw的值设置为WAKEFULNESS_AWAKE,表示屏幕状态为Awake。然后通过Notifier#onWakefulnessChangeStarted()方法进行屏幕状态开始改变但未完成切断的工作(如亮灭屏广播)。

1.2.Notifier#onWakefulnessChangeStarted()

// frameworks/base/services/core/java/com/android/server/power/Notifier.java

    public void onWakefulnessChangeStarted(final int wakefulness, int reason, long eventTime) {

        // 是否为可交互状态

        final boolean interactive = PowerManagerInternal.isInteractive(wakefulness);

        ......

        if (mInteractive != interactive) {

            // Finish up late behaviors if needed.

            if (mInteractiveChanging) {

                handleLateInteractiveChange();

            }

            .......

            // 更新交互状态

            mInteractive = interactive;

            mInteractiveChangeReason = reason;

            mInteractiveChangeStartTime = eventTime;

            // 交互状态开始变化

            mInteractiveChanging = true;

            // 进行交状态变化早期任务

            handleEarlyInteractiveChange();

        }

    }

首先更新mInteractive值,表示系统是否可交互,并将交互状态设置给Input模块。当PMS唤醒状态为Awake和Dreaming时,认为是可交互状态。然后调用handleEarlyInteractiveChange()方法,进行交状态变化后的早期任务。

1.3.handleEarlyInteractiveChange()进行交互状态变化后的早期任务

// frameworks/base/services/core/java/com/android/server/power/Notifier.java

    private void handleEarlyInteractiveChange() {

        synchronized (mLock) {

            if (mInteractive) {  // 可交互状态

                // 在system_server主线程进行

                mHandler.post(new Runnable() {

                    @Override

                    public void run() {

                        final int why = translateOnReason(mInteractiveChangeReason);

                        // 通知PhoneWindowManager开始亮屏

                        mPolicy.startedWakingUp(why);

                    }

                });

                // 表示处于哪种交互状态变化过程中

                mPendingInteractiveState = INTERACTIVE_STATE_AWAKE;

                // 表示将要发送亮屏广播

                mPendingWakeUpBroadcast = true;

                // 发送亮屏广播

                updatePendingBroadcastLocked();

            } else {

                // Going to sleep...

                ......

            }

        }

    }

亮屏时属于可交互状态,mInteractive此时为true,因此调用PhoneWindowManager#startedWakingUp()方法,该方法中将会通知锁屏等组件进行相应操作,需要注意的是,这个调用是在system_server主线程进行。然后通过updatePendingBroadcastLocked()方法发送亮屏广播。

1.4.updatePendingBroadcastLocked()发送亮屏广播

该方法负责亮灭屏广播的发送:

// frameworks/base/services/core/java/com/android/server/power/Notifier.java

    private void updatePendingBroadcastLocked() {

        if (!mBroadcastInProgress

                && mPendingInteractiveState != INTERACTIVE_STATE_UNKNOWN

                && (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast

                        || mPendingInteractiveState != mBroadcastedInteractiveState)) {

            mBroadcastInProgress = true;                        // 表示处于广播发送过程中

            mSuspendBlocker.acquire();                          // 申请SuspendBlocker,防止CPU休眠

            Message msg = mHandler.obtainMessage(MSG_BROADCAST); // system_server中进行发送亮屏广播流程

            msg.setAsynchronous(true);

            mHandler.sendMessage(msg);

        }

    }

这里会先进行条件判断,确认是否满足发送广播条件,这几个条件含义如下:

mBroadcastInProgress:是否正在进行广播发送;

mPendingInteractiveState:即将发送广播的状态值:INTERACTIVE_STATE_UNKNOWN为默认值,INTERACTIVE_STATE_AWAKE表示亮屏广播,INTERACTIVE_STATE_ASLEEP表示灭屏广播;

mBroadcastedInteractiveState:表示当前广播的状态值;

mPendingWakeUpBroadcast:表示将要发送亮屏广播;

mPendingGoToSleepBroadcast:表示将要发送灭屏广播。

进入此方法后,首先申请一个SuspendBlocker锁,目的是避免在发送广播过程中出现CPU进入休眠状态导致广播发送失败。之后通过Handler中调用sendNextBroadcaset()方法发送广播:

// frameworks/base/services/core/java/com/android/server/power/Notifier.java

    private void sendNextBroadcast() {

        final int powerState;

        synchronized (mLock) {

            // 首次执行

            if (mBroadcastedInteractiveState == INTERACTIVE_STATE_UNKNOWN) {

                ......

            } else if (mBroadcastedInteractiveState == INTERACTIVE_STATE_AWAKE) {

                //如果当前广播状态为INTERACTIVE_STATE_AWAKE,可能会发送灭屏广播

                if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast

                        || mPendingInteractiveState == INTERACTIVE_STATE_ASLEEP) {

                    mPendingGoToSleepBroadcast = false;

                    mBroadcastedInteractiveState = INTERACTIVE_STATE_ASLEEP;

                } else {

                    // 结束亮屏广播发送

                    finishPendingBroadcastLocked();

                    return;

                }

            } else {// 如果当前广播状态为INTERACTIVE_STATE_ASLEEP送亮屏广播

                if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast

                        || mPendingInteractiveState == INTERACTIVE_STATE_AWAKE) {

                    mPendingWakeUpBroadcast = false;

                    // 更新mBroadcastedInteractiveState值

                    mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;

                } else {

                    // 结束灭屏广播发送

                    finishPendingBroadcastLocked();

                    return;

                }

            }

            powerState = mBroadcastedInteractiveState;

        }

        if (powerState == INTERACTIVE_STATE_AWAKE) {

            // 发送亮屏广播

            sendWakeUpBroadcast();

        } else {

            // 发送灭屏广播

            sendGoToSleepBroadcast();

        }

    }

最终,通过sendWakeUpBroadcast()方法,发送亮屏广播:

// frameworks/base/services/core/java/com/android/server/power/Notifier.java

private void sendWakeUpBroadcast() {

    // 发送Intent.ACTION_SCREEN_ON广播

    if (mActivityManagerInternal.isSystemReady()) {

        mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,

                mWakeUpBroadcastDone, mHandler, 0, null, null);

    }

    ......

}

这里指定了一个mWakeUpBroadcastDone,会在广播发送后,作为最后一个广播接收器接收该广播,进行广播发送完毕后的工作。mWakeUpBroadcastDone中则继续执行sendNextBroadcast()方法,在这次执行时,根据条件会执行inishPendingBroadcastLocked()方法:

private void finishPendingBroadcastLocked() {

    // 重置mBroadcastInProgress,表示当前没有正在进行发送的广播

    mBroadcastInProgress = false; 

    // 释放SuspendBlocker锁

    mSuspendBlocker.release(); 

}

从而完成亮屏广播的发送。

1.5.userActivityNoUpdateLocked()更新用户交互时间

回到wakeUpNoUpdateLocked()方法中,setWakefulnessLocked()执行完毕后,执行userActivityNoUpdateLocked()方法,此方法用来更新系统和用户最后的交互时间,根据这个时间可以决定何时自动休眠。详细内容在自动灭屏流程中分析。

此时wakeUpNoUpdateLocked()执行完毕并返回true,接下来执行updatePowerStateLocked()方法。

2.updatePowerStateLocked()更新PMS全局状态

此方法在第一篇已进行了分析,这里只看进行亮屏的最关键操作——执行updateDisplayPowerStateLocked()方法。

2.1.updateDisplayPowerStateLocked()向DMS发起请求

PMS中所有请求数据会封装到DisplayPowerRequest对象中,向DMS中发起请求。DMS中收到请求后,在DisplayPowerController中更新Display状态和亮度,并将请求结果返回给PMS。DisplayPowerController中处理请求逻辑如下:

public boolean requestPowerState(DisplayPowerRequest request,

        boolean waitForNegativeProximity) {

    synchronized (mLock) {

        boolean changed = false;

        // 是否需要等待PSensor上报远离事件

        if (waitForNegativeProximity

                && !mPendingWaitForNegativeProximityLocked) {

            mPendingWaitForNegativeProximityLocked = true;

            changed = true;

        }

        // 判断是否是新的请求

        if (mPendingRequestLocked == null) {

            mPendingRequestLocked = new DisplayPowerRequest(request);

            changed = true;

        } else if (!mPendingRequestLocked.equals(request)) {

            mPendingRequestLocked.copyFrom(request);

            changed = true;

        }

        if (changed) {

            mDisplayReadyLocked = false;

        }

        // 开始处理请求

        if (changed && !mPendingRequestChangedLocked) {

            mPendingRequestChangedLocked = true;

            sendUpdatePowerStateLocked();

        }

        // 返回给PMS,表示请求是否处理完成

        return mDisplayReadyLocked;

    }

}

这个方法中,会判断请求时携带的DisplayPowerRequest对象是否和上一次发生请求的DisplayRequest对象相同,如果不同表示发生了的请求,则开始处理这次新请求,并向PMS返回mDisplayReadyLocked,该值表示DMS中是否处理完毕请求。

当请求处理完毕后,会将mDisplayReadyLocked值置为true,同时回调PMS#onStateChanged()方法通知PMS更新完成。

经过这一步后,会将系统的亮度、显示状态全部设置完毕,此时屏幕已经亮了。关于亮度和显示状态的详细分析

2.2.finishWakefulnessChangeIfNeededLocked()进行唤醒状态变化完成时任务

回到updatePowerStateLocked()方法中,当DMS处理请求完成,并且返回请求结果后,执行finishWakefulnessChangeIfNeededLocked()方法,进行屏幕状态改变完成后的操作:

private void finishWakefulnessChangeIfNeededLocked() {

        // 通过Notifier进行系统状态wakefulness改变后的处理

        if (mWakefulnessChanging && mDisplayReady) {

            if (getWakefulnessLocked() == WAKEFULNESS_DOZING

                    && (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) {

                return;

            } else {

                mDozeStartInProgress = false;

            }

            // 唤醒状态变化完成

            mWakefulnessChanging = false;

            mNotifier.onWakefulnessChangeFinished();

        }

    }

这里会执行Notifier#onWakefulnessChangeFinished()方法,通知Notifier做唤醒状态改变完成后,交互状态相关的一些任务。

2.3.Notifier#onWakefulnessChangeFinished()

// frameworks/base/services/core/java/com/android/server/power/Notifier.java

    public void onWakefulnessChangeFinished() {

        // 将mInteractiveChanging设置为false,表示交互状态也改变完成

        if (mInteractiveChanging) {

            mInteractiveChanging = false;

            // 处理进行交互状态变化完成后的任务

            handleLateInteractiveChange();

        }

    }

这里将mInteractiveChanging值重置为false,表示交互状态的变化已经完成,接下来执行handleLateInteractiveChange()方法。

2.4.handleLateInteractiveChange()进行交互状态变化完成后的任务

handleLateInteractiveChange()方法和handleEarlyInteractiveChange()方法相对应,一个处理交互状态改变后的早期工作,一个处理交互状态改变后的后期工作:

// frameworks/base/services/core/java/com/android/server/power/Notifier.java

    private void handleLateInteractiveChange() {

        synchronized (mLock) {

            if (mInteractive) {

                mHandler.post(new Runnable() {

                    @Override

                    public void run() {

                        // 通知PhoneWindowManager完成亮屏

                        mPolicy.finishedWakingUp(why);

                    }

                });

            } else {

                // 不可交互状态时流程

                ......

            }

        }

    }

在system_server主线程中,通知PhoneWindowManager完成亮屏。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336

推荐阅读更多精彩内容