Android PowerManagerService--03:亮屏流程

本文转载自:Android R PowerManagerService模块(3) 亮屏流程

本文基于Android 11源码分析

前言

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

// frameworks/base/core/java/android/os/PowerManager.java
/**
 * @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()方法,在这次执行时,根据条件会执行finishPendingBroadcastLocked()方法:

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中处理请求逻辑如下:

// frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java

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()方法,进行屏幕状态改变完成后的操作:

// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
    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完成亮屏。

3.总结

  亮屏过程中,关键步骤如下:

  1. 更新PMS唤醒状态;

  2. 更新交互状态;

  3. system_server主线程执行PhoneWindowManager#startedWakingUp()方法,通知WMS开始亮屏;

  4. system_server主线程发送亮屏广播;

  5. 向DMS请求Display状态和亮度;

  6. DMS返回请求状态后,system_server主线程执行PhoneWindowManager#finishedWakingUp()通知WMS模块完成亮屏。

  整个过程时序图如下:

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

推荐阅读更多精彩内容