Android R PowerManagerService模块(3) 亮屏流程

4,441 阅读8分钟

亮屏有很多种方式,如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更新完成。 经过这一步后,会将系统的亮度、显示状态全部设置完毕,此时屏幕已经亮了。关于亮度和显示状态的详细分析,见 Android R DisplayManagerService模块(3) DMS部分亮灭屏流程

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完成亮屏。

3.总结

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

  1. 更新PMS唤醒状态;
  2. 更新交互状态;
  3. system_server主线程执行PhoneWindowManager#startedWakingUp()方法,通知WMS开始亮屏;
  4. system_server主线程发送亮屏广播;
  5. 向DMS请求Display状态和亮度;
  6. DMS返回请求状态后,system_server主线程执行PhoneWindowManager#finishedWakingUp()通知WMS模块完成亮屏。

整个过程时序图如下:

wakeup-power.jpg