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 ;
}
//是否点亮屏幕
final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != ;
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 != ) {
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) == ) {
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) != && 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 <= ) { //息屏时,按下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, );
}
//最终将调用到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 += ;
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 ? : , 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 == ) {
//原生不进入
powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
} else if (count == ) { //原生不进入
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, );
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 = ;
if (mPowerKeyWakeLock.isHeld()) {
mPowerKeyWakeLock.release();
}
}

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

三、Power按键处理总流程

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

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

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

    一:Android处理Power按键长按操作 在Framework层中,Android4.x对Power键(KeyEvent.KEYCODE_POWER)的操作,我们从PhoneWindowManag ...

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

    二,关机流程 从前一篇博文我们知道,当用户长按Power键时会弹出(关机.重启,飞行模式等选项)对话框,我们点击关机,则会弹出关机确认对话框.那么从选项对话框到关机确认对话框又是一个什么流程呢.下面我 ...

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

    本文转载自:http://blog.csdn.net/kris_fei/article/details/77894406 Rockchip的按键驱动位于 kernel/drivers/input/ke ...

  4. Android 7.0 中 ContentProvider 实现原理

    欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 作者:汪毅雄 导语: 本文描述了ContentProvider发布者和调用者这两在Framework层是如何实现的. 作为Android的四大 ...

  5. Android下添加新的自定义键值和按键处理流程

            Android下添加新的自定义键值和按键处理流程     说出来不怕大家笑话,我写这篇博客的原因在于前几天去一个小公司面试Android系统工程师,然后在面试的时候对方的技术总监问了我 ...

  6. Android下添加新的自定义键值和按键处理流程【转】

    本文转载自: Android下添加新的自定义键值和按键处理流程     说出来不怕大家笑话,我写这篇博客的原因在于前几天去一个小公司面试Android系统工程师,然后在面试的时候对方的技术总监问了我一 ...

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

    刚接触Android开发的时候,对touch.key事件的处理总是一知半解,一会是Activity里的方法,一会是各种View 中的,自己始终不清楚到底哪个在先哪个在后,总之对整个处理流程没能很好的把 ...

  8. Android:Mstar Android8.0平台音量控制流程

    一.Speaker 音量.静音流程分析 java层音量设置首先调用到的是AudioManager.java中的方法,在这里有两种方法可以设置音量 setStreamVolume 和 adjustStr ...

  9. Android Tv 中的按键事件 KeyEvent 分发处理流程

    这次打算来梳理一下 Android Tv 中的按键点击事件 KeyEvent 的分发处理流程.一谈到点击事件机制,网上资料已经非常齐全了,像什么分发.拦截.处理三大流程啊:或者 dispatchTou ...

随机推荐

  1. 玲珑杯 Round #11 (1001 1004 1007)

    比赛链接 直接贴代码.. #include<bits/stdc++.h> using namespace std; typedef long long LL; int main() { L ...

  2. (转)ORACLE中SID和SERVICE_NAME的区别

    背景:之前一直分不清plsql和程序中配置文件url之间的连接,想当然的认为service_name 和jdburl后面的实例相对应,直到出错的这一天,通过这篇博客,彻底扫除了盲点. 1 问题 1.1 ...

  3. (转)JAVA排序汇总

    JAVA排序汇总 package com.softeem.jbs.lesson4; import java.util.Random; /** * 排序测试类 * * 排序算法的分类如下: * 1.插入 ...

  4. openstack中使用linux_bridge实现vxlan网络

    openstack环境: 1 版本:ocata 2 系统:ubuntu16.04.2 3 控制节点 1个 + 计算节点 1个 4 控制节点网卡为ens33,ip = 172.171.5.200 ens ...

  5. 进阶篇之纯css+字体实现五角星(半颗星)评分

    1.前言 之前写了一篇实现五角星打分效果的demo.这个demo用来实现打分效果绰绰有余,那么有时候我们在统计评分的时候,就会有半颗星或者1/3颗星星这样的那要如何实现呢?来来来,纯字体+css实现! ...

  6. kafka生产实践

    最近接触到一个APP流量分析的项目,类似于友盟.涉及到几个C端高并发的接口,这几个接口主要用于C端数据的提交.在没有任何缓冲的情况下,一个接口涉及到5张表的提交.压测的结果很不理想,主要瓶颈就在与RD ...

  7. VM虚拟机中安装Linux操作系统

    本文操作步骤,笔者已实验成功 (前提:正确安装VM并激活) 1,点击新建虚拟机,在页面上选择"自定义",点击下一步 2,进入选择虚拟机硬件兼容页面,这里一般不用操作,直接点击下一步 ...

  8. Uva 122 树的层次遍历 Trees on the level lrj白书 p149

    是否可以把树上结点的编号,然后把二叉树存储在数组中呢?很遗憾如果结点在一条链上,那将是2^256个结点 所以需要采用动态结构 首先要读取结点,建立二叉树addnode()+read_input()承担 ...

  9. ASP.NET Core MVC Tag Helpers 介绍

    简介 Tag Helpers 提供了在视图中更改和增强现有HTML元素的功能.将它们添加到视图中,会经过Razor模板引擎处理并创建一个HTML,之后再返回给浏览器.有一些Tag Helpers,其实 ...

  10. 使用Travis CI自动部署Hexo博客

    自从使用GitHub Pages和Hexo来发布博客之后,不得不说方便了许多,只需要几个简单的命令博客就发布了.但在不断的使用中发现每次的发布操作也挺耗时的. 我一般的操作是将平时整理好的md文件放到 ...