zoukankan      html  css  js  c++  java
  • 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按键处理总流程

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

     

     

  • 相关阅读:
    mysql 取出每科成绩前两名
    mysql 数据库以及sql 的优化
    Twitter开源分布式自增ID算法snowflake
    SVN 冲突
    VUE 入门 1 列表、if判断 、双向绑定
    Roadblock
    最大子序和
    SOLDIERS
    绿豆蛙的归宿
    Place the Robots
  • 原文地址:https://www.cnblogs.com/dyufei/p/7467353.html
Copyright © 2011-2022 走看看