PowerManager学习笔记-Power键灭屏流程

本文基于Android S源码梳理

系列回顾

PowerManager学习笔记-Power键亮屏流程

学习目标

通过AOSP源码,对Power键灭屏流程进行梳理,比较亮屏之间的差异。

Power键灭屏简介

上一篇Power键亮屏流程中,已经通过源码简单了解了整个流程,包括事件处理、PowerManager侧处理,DPC侧亮度处理。本篇将继续进行Power键灭屏流程的梳理。由于亮屏和灭屏有部分逻辑重叠,如设置屏幕亮度动画等,这里重叠的部分会一带而过,重点去梳理一些差异部分。

按键亮灭屏框架: image.png Power键灭屏流程图: 未命名文件 (2).jpg 简要概括一下Power键的灭屏流程,当用户在亮屏状态下按下Power键,InputReader从EventHub中读取到事件,交由InputDispatcher进行分发,通知PhoneWindowManager对此事件进行处理,PhoneWindowManager处理后,通知PowerManagerService进行wakeUp处理,随后PowerManagerService在内部更新wakefulness,再通知DisplayPowerController进行亮屏状态更新,此间DisplayPowerController同样也存在阻塞灭屏,等待系统callback通知后,进行屏幕状态动画与亮度设置动画,完成后通知锁屏灭屏,并发送灭屏广播。

Power键灭屏时序图: Power键灭屏时序图.jpg 同样到此处,如果只是对Power键灭屏流程了解一下的同学,可以不用往下看了,下面就是具体业务中重要环节的详细介绍。

业务梳理

相关类

  • frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
  • frameworks/base/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
  • frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
  • frameworks/base/services/core/java/com/android/server/power/Notifier.java
  • frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
  • frameworks/base/services/core/java/com/android/server/display/RampAnimator.java
  • frameworks/base/services/core/java/com/android/server/display/ColorFade.java
  • frameworks/base/services/core/java/com/android/server/display/DisplayPowerState.java
  • frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java
  • frameworks/base/services/core/java/com/android/server/display/LocalDisplayAdapter.java
  • frameworks/base/services/core/java/com/android/server/lights/LightsService.java

部分类名解释和缩写

类名 作用 缩写
SingleKeyGestureDetector Android 12新增处理多屏按键响应
PowerManagerService Power状态管理 PMS
DisplayPowerController 管理设备Display状态,主要处理近距sensor,亮灭屏 DPC
Notifier Power侧用于通知其他系统模块

1.Power键事件处理差异

随着多屏设备的出现,google也在Android S上体现出他们对此类设备的适配理解,原先在Android S之前,处理Power键灭屏的流程还是放在对其Key Event处理中实现,而在Android S上,这个逻辑被移到SingleKeyGestureDetector中进行处理:

frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

// TODO(b/117479243): handle it in InputPolicy
/** {@inheritDoc} */
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    ...
    // This could prevent some wrong state in multi-displays environment,
    // the default display may turned off but interactive is true.
    // 这里注释是为了解决多屏显示的场景下出现错误的状态
    final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
    final boolean interactiveAndOn = interactive && isDefaultDisplayOn;
    if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
        handleKeyGesture(event, interactiveAndOn);
    }
    ...
}

private void handleKeyGesture(KeyEvent event, boolean interactive) {
    ...
    mSingleKeyGestureDetector.interceptKey(event, interactive);
}
复制代码

从PhoneWindowManager的处理可以看出,调用到了SingleKeyGestureDetector:

frameworks/base/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java

void interceptKey(KeyEvent event, boolean interactive) {
    if (event.getAction() == KeyEvent.ACTION_DOWN) {
        // Store the non interactive state when first down.
        if (mDownKeyCode == KeyEvent.KEYCODE_UNKNOWN || mDownKeyCode != event.getKeyCode()) {
            mBeganFromNonInteractive = !interactive;
        }
        interceptKeyDown(event);
    } else {
        interceptKeyUp(event);
    }
}

private boolean interceptKeyUp(KeyEvent event) {
        ...
        // This could be a multi-press.  Wait a little bit longer to confirm.
        mKeyPressCounter++;
        Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode,
                mKeyPressCounter, downTime);
        msg.setAsynchronous(true);
        mHandler.sendMessageDelayed(msg, MULTI_PRESS_TIMEOUT);
        ...
}

复制代码

在消息MSG_KEY_DELAYED_PRESS中,调用接口onMultiPress,此接口同样也被PhoneWindowManager的内部类PowerKeyRule实现,并在其方法中调用了powerPress方法:

frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

private void powerPress(long eventTime, int count, boolean beganFromNonInteractive) {
        ...
        switch (mShortPressOnPowerBehavior) {
            ...
            case SHORT_PRESS_POWER_GO_TO_SLEEP:
                sleepDefaultDisplayFromPowerButton(eventTime, 0);
                break;
            ...
}

private boolean sleepDefaultDisplayFromPowerButton(long eventTime, int flags) {
    ...
    sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
    ...
}

private void sleepDefaultDisplay(long eventTime, int reason, int flags) {
    mRequestedOrSleepingDefaultDisplay = true;
    mPowerManager.goToSleep(eventTime, reason, flags);
}

复制代码

最终调用了PMS的goToSleep接口,将业务传递到PMS侧进一步处理。 总结一下,在Android S中,google对Power键灭屏的流程基本思路还是和原来一样的,只不过为了去处理多屏事件处理时可能产生的状态异常,增加了一个SingleKeyGestureDetector来辅助PhoneWindowManager进行处理。最终还是通过PMS接口goToSleep完成对Power KeyEvent的处理。

2.PMS侧处理差异

开篇的时候提到的Power键灭屏的流程可以看到,和亮屏基本上没有区别,流程都是PMS先更新相关状态,如wakefulness,然后通知DPC进行屏幕状态更新,并设置亮度动画。所以这一块的流程就不再去梳理细节,需要的可以参考亮屏流程中的梳理。 但是也有不相同的地方,灭屏广播的触发时机,在亮屏流程中,亮屏广播发生在PMS通知DPC亮屏之前,而灭屏流程中,同样也存在相应的灭屏广播,灭屏广播的发送时间是在通知DPC处理之后。

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private void updatePowerStateLocked() {
        ...
        // Phase 3: Update display power state.
        // 通知DPC更新屏幕状态
        final boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
        ...
        // Phase 5: Send notifications, if needed.
        // 向系统模块更新Power状态,其中也包括触发灭屏广播
        finishWakefulnessChangeIfNeededLocked();
        ...
}

private void finishWakefulnessChangeIfNeededLocked() {
        ...
        mNotifier.onWakefulnessChangeFinished();
}
复制代码

Notifier是PMS框架中专门用于通知其他系统模块PMS状态发生变化的模块,同时也包括发送亮灭屏广播。

frameworks/base/services/core/java/com/android/server/power/Notifier.java
public void onWakefulnessChangeFinished() {
    if (DEBUG) {
        Slog.d(TAG, "onWakefulnessChangeFinished");
    }

    if (mInteractiveChanging) {
        mInteractiveChanging = false;
        handleLateInteractiveChange();
    }
}


private void handleLateInteractiveChange() {
            // Finished going to sleep...
            // This is a good time to make transitions that we don't want the user to see,
            // such as bringing the key guard to focus.  There's no guarantee for this
            // however because the user could turn the device on again at any time.
            // Some things may need to be protected by other mechanisms that defer screen on.

            ...
            updatePendingBroadcastLocked();
        }
    }
}
复制代码

之所以需要将灭屏广播移到DPC处理之后,在handleLateInteractiveChange中注释也说明了这一点,因为此时通知系统中的其他模块,可以进行一些不希望用户看到的业务逻辑处理。

3.DPC侧处理差异

DPC主要作用就是更新屏幕状态,执行亮度动画,本节中主要梳理的差异是阻塞灭屏,在亮屏流程中,当DPC准备好屏幕状态后,需要通过阻塞亮屏来等待WMS和锁屏的绘制,以确保屏幕点亮后能够正常显示。而在灭屏的过程中DPC也同样存在阻塞灭屏的流程:

frameworks/base/services/core/java/com/android/server/display/DisplayPowerController.java
private boolean setScreenState(int state, boolean reportOnly) {
    final boolean isOff = (state == Display.STATE_OFF);

    if (mPowerState.getScreenState() != state
            || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
        // If we are trying to turn screen off, give policy a chance to do something before we
        // actually turn the screen off.
        if (isOff && !mScreenOffBecauseOfProximity) {
            if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON
                    || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
                setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF);
                // 阻塞灭屏
                blockScreenOff();
                // 通知PhoneWindowManager开始灭屏
                mWindowManagerPolicy.screenTurningOff(mDisplayId, mPendingScreenOffUnblocker);
                // 解除阻塞亮屏
                unblockScreenOff();
            } else if (mPendingScreenOffUnblocker != null) {
                // Abort doing the state change until screen off is unblocked.
                return false;
            }
        }
        ...
}

private void blockScreenOff() {
    if (mPendingScreenOffUnblocker == null) {
        Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
        mPendingScreenOffUnblocker = new ScreenOffUnblocker();
        mScreenOffBlockStartRealTime = SystemClock.elapsedRealtime();
        Slog.i(TAG, "Blocking screen off");
    }
}

private final class ScreenOffUnblocker implements WindowManagerPolicy.ScreenOffListener {
    // 通过源码查询,调用此callback的地方只有TaskSnapshotController,用于在灭屏的时候保存当前task状态。
    @Override
    public void onScreenOff() {
        Message msg = mHandler.obtainMessage(MSG_SCREEN_OFF_UNBLOCKED, this);
        mHandler.sendMessage(msg);
    }
}
复制代码

当完成后,会再次调用updatePowerState来继续更新屏幕状态,并完成亮度动画设置。 其余的部分基本流程都和亮屏的流程相似,这里也不重复整理了。

猜你喜欢

转载自juejin.im/post/7041907301514477599
今日推荐