Android7.0 PowerManagerService(4) Power按键流程

来源:互联网 发布:淘宝看买家退货率 编辑:程序博客网 时间:2024/05/22 23:59

按键的处理主要由InputManagerService负责,属于Android输入系统的流程。在这篇博客里,我们只关注与Power键相关的内容。InputManagerService处理的按键事件,最终将会传递到PhoneWindowManager的interceptKeyBeforeQueueing函数。
我们就从这个函数开始,逐步进行分析。

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {    if (!mSystemBooted) {        // If we have not yet booted, don't let key events do anything.        return 0;    }    //表示屏幕是否点亮    final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;    final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;    final boolean canceled = event.isCanceled();    //按键对应的编码    final int keyCode = event.getKeyCode();    .................    switch (keyCode) {        ...........        case KeyEvent.KEYCODE_POWER: {            result &= ~ACTION_PASS_TO_USER;            isWakeKey = false; // wake-up will be handled separately            if (down) {                //处理按下Power键                interceptPowerKeyDown(event, interactive);            } else {                //处理松开Power键                interceptPowerKeyUp(event, interactive, canceled);            }             break;        }    }    ..............    if (isWakeKey) {        //按power键时,isWakeKey置为false,于是不会调用wakeUp函数        wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY");    }    return result;}

接下来,我们分别看一下interceptPowerKeyDown和interceptPowerKeyUp函数。

一、interceptPowerKeyDown

interceptPowerKeyDown用于处理按下Power键(还未松手释放)对应的事件。

private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {    // Hold a wake lock until the power key is released.    // mPowerKeyWakeLock为PARTIAL_WAKE_LOCK级别的锁    if (!mPowerKeyWakeLock.isHeld()) {        //将调用到PMS的acquire WakeLock流程        mPowerKeyWakeLock.acquire();    }    // Cancel multi-press detection timeout.    //处理多次按power键的场景    //每次power up时,发送MSG_POWER_DELAYED_PRESS的延迟消息    //如果延迟消息被处理,说明一次完整的Power键处理结束(按下去,弹起来)    //在延迟消息被处理前,再次按power键,就检测到多次点击了    //实际上,原生终端不支持该场景    if (mPowerKeyPressCounter != 0) {        mHandler.removeMessages(MSG_POWER_DELAYED_PRESS);    }    // Detect user pressing the power button in panic when an application has    // taken over the whole screen.    // 从注释来看及mHiddenNavPanic的代码,觉得像是处理“误触”的    boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive,        SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags));    if (panic) {        mHandler.post(mHiddenNavPanic);    }    // Latch power key state to detect screenshot chord.    // 如果当前是亮屏状态,且满足触发截屏的条件,触发截屏功能    if (interactive && !mScreenshotChordPowerKeyTriggered            && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {        mScreenshotChordPowerKeyTriggered = true;        mScreenshotChordPowerKeyTime = event.getDownTime();        interceptScreenshotChord();    }    // Stop ringing or end call if configured to do so when power is pressed.    TelecomManager telecomManager = getTelecommService();    boolean hungUp = false;    if (telecomManager != null) {        if (telecomManager.isRinging()) {            // Pressing Power while there's a ringing incoming            // call should silence the ringer.            //如果有电话拨入,且电话铃声响起,按Power键,设置电话响铃静音            telecomManager.silenceRinger();        } else if ((mIncallPowerBehavior            & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0            && telecomManager.isInCall() && interactive) {            // Otherwise, if "Power button ends call" is enabled,            // the Power button will hang up any current active call.            //如果正在接听电话,且配置了Power键挂断电话的话,按Power按键挂断正在接听的电话            hungUp = telecomManager.endCall();        }    }    GestureLauncherService gestureService = LocalServices.getService(            GestureLauncherService.class);    boolean gesturedServiceIntercepted = false;    if (gestureService != null) {        //手势对应的服务,尝试拦截处理Power键动作事件        gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive,                mTmpBoolean);        if (mTmpBoolean.value && mGoingToSleep) {            mCameraGestureTriggeredDuringGoingToSleep = true;        }    }    // If the power key has still not yet been handled, then detect short    // press, long press, or multi press and decide what to do.    // Power键事件的处理,就像处理屏幕上的点击事件一样,也依赖于事件分发机制    // 如果已经被消耗掉了,就不会再被继续处理    mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered            || mScreenshotChordVolumeUpKeyTriggered || gesturedServiceIntercepted;    //Power键事件未被消耗掉    if (!mPowerKeyHandled) {        //屏幕还是亮的        if (interactive) {            // When interactive, we're already awake.            // Wait for a long press or for the button to be released to decide what to do.            //1、 判断是否支持长按的行为            if (hasLongPressOnPowerBehavior()) {                //2、 亮屏时,长按Power键将触发MSG_POWER_LONG_PRESS消息                Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);                msg.setAsynchronous(true);                mHandler.sendMessageDelayed(msg,                        ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());            }        } else {            //此时屏幕是熄灭状态            //3、先唤醒系统,这个会调用到PMS的wakeUp            wakeUpFromPowerKey(event.getDownTime());            //支持熄屏长按,mSupportLongPressPowerWhenNonInteractive读资源文件得到,默认为false            if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {                Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);                msg.setAsynchronous(true);                mHandler.sendMessageDelayed(msg,                        ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());                mBeganFromNonInteractive = true;            } else {                //默认返回1                final int maxCount = getMaxMultiPressPowerCount();                if (maxCount <= 1) {                    //息屏时,按下power键(不弹起),仅消耗掉该事件                    mPowerKeyHandled = true;                } else {                    mBeganFromNonInteractive = true;                }            }        }    }}

1、hasLongPressOnPowerBehavior
hasLongPressOnPowerBehavior负责判断终端是否支持长按的行为:

private boolean hasLongPressOnPowerBehavior() {    return getResolvedLongPressOnPowerBehavior() != LONG_PRESS_POWER_NOTHING;}private int getResolvedLongPressOnPowerBehavior() {    //取决与系统属性"factory.long_press_power_off",此处默认为false    if (FactoryTest.isLongPressOnPowerOffEnabled()) {        return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;    }    return mLongPressOnPowerBehavior;}

从上面的代码可以看出,终端是否支持长按行为,最终将由mLongPressOnPowerBehavior决定。

.........mLongPressOnPowerBehavior = mContext.getResources().getInteger(        com.android.internal.R.integer.config_longPressOnPowerBehavior);.........

mLongPressOnPowerBehavior将在PhoneWindowManager初始化时,通过读取资源文件得到,一般情况下应该为1。
于是,hasLongPressOnPowerBehavior的值返回true,即终端支持Power键长按。

2、MSG_POWER_LONG_PRESS的处理
从上面的代码,我们知道亮屏时按Power键,会触发延迟的MSG_POWER_LONG_PRESS消息。
如果在MSG_POWER_LONG_PRESS超时前,Power键未被释放掉,那么此次操作被定义为长按Power键。

MSG_POWER_LONG_PRESS对应的处理函数为powerLongPress:

private void powerLongPress() {    //也是由资源文件得到,默认为1,即LONG_PRESS_POWER_GLOBAL_ACTIONS    final int behavior = getResolvedLongPressOnPowerBehavior();    switch (behavior) {        case LONG_PRESS_POWER_NOTHING:            break;        case LONG_PRESS_POWER_GLOBAL_ACTIONS:            mPowerKeyHandled = true;            //感觉这里是:终端对接收的事件处理后,给用户一个反馈信息            //performHapticFeedbackLw主要进行震动反馈,例如按键后,终端震动一下            //不同的事件,定义了不同的震动模式            if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {                //如果没有震动反馈,这里尝试进行声音的反馈,例如响一下按键音                performAuditoryFeedbackForAccessibilityIfNeed();            }            //弹出选择关机还是重启的对话框            showGlobalActionsInternal();            break;        .........    }}

3、wakeUpFromPowerKey
在息屏的状态下按下Power键,将调用wakeUpFromPowerKey函数唤醒系统:

private void wakeUpFromPowerKey(long eventTime) {    //从config.xml来看,mAllowTheaterModeWakeFromPowerKey默认为true    wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, "android.policy:POWER");}private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, String reason) {    //取数据库的值    final boolean theaterModeEnabled = isTheaterModeEnabled();    //按Power键时,条件返回false    if (!wakeInTheaterMode && theaterModeEnabled) {        return false;    }    //看来唤醒时,将退出剧院模式    if (theaterModeEnabled) {        Settings.Global.putInt(mContext.getContentResolver(),                Settings.Global.THEATER_MODE_ON, 0);    }    //最终将调用到PMS的wakeUp函数    mPowerManager.wakeUp(wakeTime, reason);    return true;}

我们跟进一下PMS的wakeUp函数:

public void wakeUp(long eventTime, String reason, String opPackageName) {    ..........    try {        wakeUpInternal(eventTime, reason, uid, opPackageName, uid);    } finally {        ...............    }}private void wakeUpInternal(long eventTime, String reason, int uid, String opPackageName,        int opUid) {    synchronized (mLock) {        //更新Wakefullness的状态为WAKEFULNESS_AWAKE,记录一次UserActivity        if (wakeUpNoUpdateLocked(eventTime, reason, uid, opPackageName, opUid)) {            //如之前博客所述,对整个电源状态进行一次调整,将在需要时点亮屏幕            updatePowerStateLocked();        }    }}

二、interceptPowerKeyUp
interceptPowerKeyUp处理松开Power键后的流程:

private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {    //事件被取消,或者在按下Power键时,该事件已被消耗掉    //那么就不用继续处理    final boolean handled = canceled || mPowerKeyHandled;    mScreenshotChordPowerKeyTriggered = false;    //退出截屏    cancelPendingScreenshotChordAction();    //取消MSG_POWER_LONG_PRESS事件,即在一定事件内Power键弹起,则表示这一次不是长按Power键    cancelPendingPowerKeyAction();    //从之前的代码,我们知道除了特殊功能外    //灭屏按Power键或亮屏长按时,均会消耗掉Power事件    //因此,只有亮屏短按Power键需要进行处理    if (!handled) {        // Figure out how to handle the key now that it has been released.        // 记录短按的次数        mPowerKeyPressCounter += 1;        final int maxCount = getMaxMultiPressPowerCount();        final long eventTime = event.getDownTime();        if (mPowerKeyPressCounter < maxCount) {            // This could be a multi-press.  Wait a little bit longer to confirm.            // Continue holding the wake lock.            // 与之前interceptPowerKeyDown,处理Power键被多次按下场景对应            // 每次被按下,均发送MSG_POWER_DELAYED_PRESS消息            // 实际上maxCount为1,该不会进入该分支            Message msg = mHandler.obtainMessage(MSG_POWER_DELAYED_PRESS,                interactive ? 1 : 0, mPowerKeyPressCounter, eventTime);            msg.setAsynchronous(true);            mHandler.sendMessageDelayed(msg, ViewConfiguration.getDoubleTapTimeout());            return;        }        //1、No other actions.  Handle it immediately.        powerPress(eventTime, interactive, mPowerKeyPressCounter);    }    // 2、Done.  Reset our state.    finishPowerKeyPress();}

1、powerPress
我们跟进一下powerPress函数:

private void powerPress(long eventTime, boolean interactive, int count) {    if (mScreenOnEarly && !mScreenOnFully) {        Slog.i(TAG, "Suppressed redundant power key press while "                + "already in the process of turning the screen on.");        return;    }    if (count == 2) {        //原生不进入        powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);    } else if (count == 3) {        //原生不进入        powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);    } else if (interactive && !mBeganFromNonInteractive) {        //亮屏时,将进入该分支        //mShortPressOnPowerBehavior被配置为1        switch (mShortPressOnPowerBehavior) {            case SHORT_PRESS_POWER_NOTHING:                break;            case SHORT_PRESS_POWER_GO_TO_SLEEP:                //最终调用到PMS的goToSleep函数                mPowerManager.goToSleep(eventTime,                        PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);                break;            ...............        }    }}

从上面的代码可以看出,在亮屏状态下,短按一下Power键,最终将调用到PMS的goToSleep函数,使终端进入到休眠状态,与实际情况一致。
我们跟进一下PMS的goToSleep函数:

public void goToSleep(long eventTime, int reason, int flags) {    ............    try {        goToSleepInternal(eventTime, reason, flags, uid);    } finally {        ...............    }}private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {    synchronized (mLock) {    //没有触发用户事件,将mWakefullness置为WAKEFULNESS_DOZING    if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {        //执行整体的电源状态更新,将熄灭屏幕        updatePowerStateLocked();    }}

2、finishPowerKeyPress

每当处理一次完整的Power键按下、弹出操作后,interceptPowerKeyUp调用finishPowerKeyPress进行最后的状态复位操作:

private void finishPowerKeyPress() {    mBeganFromNonInteractive = false;    mPowerKeyPressCounter = 0;    if (mPowerKeyWakeLock.isHeld()) {        mPowerKeyWakeLock.release();    }}

从代码可以看出,主要的工作其实就是将状态变量恢复为初始值,同时释放掉最初申请的锁。

三、总结

整个Power按键的主要处理流程如上图所示。结合手机的实际情况,整个逻辑还是非常好理解的。

1 0
原创粉丝点击