Android 7.0 Power 按键处理流程

Android 7.0  Power 按键处理流程

Power按键的处理逻辑由PhoneWindowManager来完成,本文只关注PhoneWindowManager中与Power键相关的内容,其他系统按键的处理类似也是在PhoneWindowManager中处理的。理解了power按键的处理再看其他系统按键的逻辑会容易的多也简单的多。

一、Power按键的上报

Power按键的上报流程与其余的按键处理流程一致,在按下power按键后驱动上报按键经InputManagerService处理按键事件,最终将会传递到PhoneWindowManager的interceptKeyBeforeQueueing函数来做具体的业务逻辑。(具体处理 Input system 做详细介绍)本篇侧重power的业务逻辑处理,这里简单介绍下power 按键的上报流程如下图:

1-4:在power 按键按下时驱动会上报按键事件,EventHub读取到事件后转给InputReader来做处理。

5-9:InputReader根据上报的事件类型(此处是按键事件),交给KeyBoardInputMapper来做按键映射,根据驱动上报的按键值来映射为android framework的按键值(即KeyEvent.KEYCODE_POWER 和相应的flag),并通知上层。

10-16:经过层调用最后到PhoneWindowManager的interceptKeyBeforeQueueing()函数来做具体的业务处理。这是本文的重点,下面具体分析。

17-18:通过PowerManagerService来唤醒系统。

二、Power 按键处理

PhoneWindowManager的interceptKeyBeforeQueueing()函数处理具体的业务逻辑,从这个函数开始进行分析。

按键的事件分为按下和抬起两个,framework的处理也是分为按下和抬起来不同的事件分别由不同的函数来处理。

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();
    //获取按键对应的android framework层的按键编码
    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;
}

扩展:由上可知 isWakeKey 用来控制是否唤醒系统并点亮屏幕,如果需要添加按键需要实现点亮屏幕功能,可以在此处理 ^_^。

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

1、Power按键按下(interceptPowerKeyDown)

interceptPowerKeyDown()用于处理按下Power键(还未抬起)对应的事件。

A: Power按键按下处理时序图

处理流程:

1)  判断是否power按键与音量按键“同时”按下需要做截屏动作,是则触发截屏。

2)  是否为响铃或通话状态,是则执行与之相关配置的动作(默认响铃会静音,如果设置按power挂断电话则挂断)。

3)  判断是否为长按,是则根据配置执行长按行为的动作。

B: 代码分析

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键处理结束(按下去,弹起来)
    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.

    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.
            //有电话拨入且响铃状态,默认设置静音
            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.
    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初始化时,通过读取资源文件(定义在frameworks/base/res/res/values/config.xm 中)得到,一般情况下应该为1。 于是,hasLongPressOnPowerBehavior的值返回true,即终端支持Power键长按。

config.xm 中定义的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;
        case LONG_PRESS_POWER_SHUT_OFF:
        case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
            mPowerKeyHandled = true;
            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
            //弹出系统关机界面
     sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
            //调关机接口
            mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
            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();
        }
    }
}

2、Power抬起(interceptPowerKeyUp )

A: Power抬起 时序图

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

B: 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函数根据屏幕状态和配置的条件,如果亮屏状态下短按power按键则调用PMS的goToSleep()函数,执行灭屏并将系统休眠。

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按键处理总流程

按键处理的总流程如下图:

时间: 2024-10-14 12:57:36

Android 7.0 Power 按键处理流程的相关文章

Android 6.0 SIM卡初始化流程

本文主要讲述Android 6.0 SIM卡初始化流程,这个过程也涉及到UICC框架的初始化,UICC(Universal Integrated Circuit Card)的架构图如下: /** * This class is responsible for keeping all knowledge about * Universal Integrated Circuit Card (UICC), also know as SIM's, * in the system. It is also

Android Framework层Power键关机流程(一,Power长按键操作处理)

一:Android处理Power按键长按操作 在Framework层中,Android4.x对Power键(KeyEvent.KEYCODE_POWER)的操作,我们从PhoneWindowManager开始分析,在分析前我这里打印了该方法的堆栈调用信息.大家可以参考一下. public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) { ...... android.uti

Android Framework层Power键关机流程(二,关机流程)

二,关机流程 从前一篇博文我们知道,当用户长按Power键时会弹出(关机.重新启动,飞行模式等选项)对话框,我们点击关机,则会弹出关机确认对话框.那么从选项对话框到关机确认对话框又是一个什么流程呢.以下我们在简单分析一下: showGlobalActionsDialog()-->showDialog()-->handleShow()-->createDialog()-->onPress()-->shutdown() PhoneWindowManager.java void s

[RK3288][Android6.0] 系统按键驱动流程分析【转】

本文转载自:http://blog.csdn.net/kris_fei/article/details/77894406 Rockchip的按键驱动位于 kernel/drivers/input/keyboard/rk_keys.c 默认支持的keys在dts中定义: 其中power key作为普通gpio,具有唤醒功能.而其他按键比如,volume up/down 可以通过adc精确读取到gpio的电压值,原理图如下:   和一般的按键一样,驱动是通过内核input子系统来将keys注册供用户

Android 4.4 Kitkat Phone工作流程浅析(十二)__4.4小结与5.0概览

前置文章: <Android 4.4 Kitkat Phone工作流程浅析(一)__概要和学习计划> <Android 4.4 Kitkat Phone工作流程浅析(二)__UI结构分析> <Android 4.4 Kitkat Phone工作流程浅析(三)__MO(去电)流程分析> <Android 4.4 Kitkat Phone工作流程浅析(四)__RILJ工作流程简析> <Android 4.4 Kitkat Phone工作流程浅析(五)__M

【转】Android 4.4 Kitkat Phone 对比 5.0 Lollipop Phone工作流程浅析

概述 在Android 4.4 中,Google 对Telephony_Phone进行了重构,前面也通过一些列文章分析了Android 4.4 中Telephony Phone的工作流程.但在2014年10月15日,Google发布了Android 5.0 预览版,正式版也在一个月之后发布.Android 5.0 变化非常大,无论从UI风格还是功能实现上,Google都进行了大刀阔斧的修改.同时,Telephony_Phone模块的架构也再次进行了调整,调整之后的Telephony_Phone各

android源码解析(二十九)--&gt;应用程序返回按键执行流程

从这篇文章中我们开始分析android系统的事件分发流程,其实网上已经有了很多关于android系统的事件分发流程的文章,奈何看了很多但是印象还不是很深,所以这里总结一番. android系统的事件分发流程分为很多部分: Native层 –> ViewRootImpl层 –> DecorView层 –> Activity层 –> ViewGroup层 –> View层 所以android系统的事件分发流程是从Native层开始的,然后分发到ViewRootImpl中,然后分发

Android按键事件处理流程 -- KeyEvent

刚接触Android开发的时候,对touch.key事件的处理总是一知半解,一会是Activity里的方法,一会是各种View 中的,自己始终不清楚到底哪个在先哪个在后,总之对整个处理流程没能很好的把握.每次写这部分代码的时候都有些心虚, 因为我不是很清楚什么时候.以什么样的顺序被调用,大都是打下log看看,没问题就算ok了.但随着时间流逝,这种感觉一直 折磨着我.期间也在网上搜索了相关资料,但总感觉不是那么令人满意.自打开始研究Android源码起,这部分内容的分析早就 被列在我的TODO l

Android 6.0 IMS流程(二)——接口扩展(从RIL到APP)

平时工作中,经常有这样的需求,就是从RIL层一直到APP,添加一个新接口给APP调用,方便系统APP或者第三方APP通过这个接口获取或者改变modem中某些的值.状态等. 基于高通平台,有三种方式去实现这种接口: 扩展ImsSenderRxr,依赖ImsService来实现. 扩展RILJ,依赖Phone进程来实现. 结合1和2,依赖Phone进程来实现. 其中,第一种方式最简单,第二种次之,第三种最复杂:但是每种方式各自有各自的限制条件以及适用范围. 本文来自http://blog.csdn.