按键的处理主要由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 separatelyif (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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

接下来,我们分别看一下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的wakeUpwakeUpFromPowerKey(event.getDownTime());//支持熄屏长按,mSupportLongPressPowerWhenNonInteractive读资源文件得到,默认为falseif (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);msg.setAsynchronous(true);mHandler.sendMessageDelayed(msg,ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());mBeganFromNonInteractive = true;} else {//默认返回1final int maxCount = getMaxMultiPressPowerCount();if (maxCount <= 1) {//息屏时,按下power键(不弹起),仅消耗掉该事件mPowerKeyHandled = true;} else {mBeganFromNonInteractive = true;}}}}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115

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

private boolean hasLongPressOnPowerBehavior() {return getResolvedLongPressOnPowerBehavior() != LONG_PRESS_POWER_NOTHING;
}private int getResolvedLongPressOnPowerBehavior() {//取决与系统属性"factory.long_press_power_off",此处默认为falseif (FactoryTest.isLongPressOnPowerOffEnabled()) {return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;}return mLongPressOnPowerBehavior;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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

.........
mLongPressOnPowerBehavior = mContext.getResources().getInteger(com.android.internal.R.integer.config_longPressOnPowerBehavior);
.........
  • 1
  • 2
  • 3
  • 4

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_ACTIONSfinal 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;.........}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

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

private void wakeUpFromPowerKey(long eventTime) {//从config.xml来看,mAllowTheaterModeWakeFromPowerKey默认为truewakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, "android.policy:POWER");
}private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, String reason) {//取数据库的值final boolean theaterModeEnabled = isTheaterModeEnabled();//按Power键时,条件返回falseif (!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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

我们跟进一下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,记录一次UserActivityif (wakeUpNoUpdateLocked(eventTime, reason, uid, opPackageName, opUid)) {//如之前博客所述,对整个电源状态进行一次调整,将在需要时点亮屏幕updatePowerStateLocked();}}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

二、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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

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被配置为1switch (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;...............}}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

从上面的代码可以看出,在亮屏状态下,短按一下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_DOZINGif (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {//执行整体的电源状态更新,将熄灭屏幕updatePowerStateLocked();}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2、finishPowerKeyPress

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

private void finishPowerKeyPress() {mBeganFromNonInteractive = false;mPowerKeyPressCounter = 0;if (mPowerKeyWakeLock.isHeld()) {mPowerKeyWakeLock.release();}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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

三、总结 

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

原文地址:http://blog.csdn.net/gaugamela/article/details/52912382

Android7.0 PowerManagerService(4) Power按键流程相关推荐

  1. Android 系统(42)---Android7.0 PowerManagerService亮灭屏分析(三)

    Android7.0 PowerManagerService亮灭屏分析(三) 在前面两部分已经对绘制windows与设置设备状态进行了详细讲解. 之后接着就该对亮度值进行设置, 实现亮屏动作了. 在D ...

  2. Android 系统(40)--Android7.0 PowerManagerService亮灭屏分析(一)

    Android7.0 PowerManagerService亮灭屏分析(一) 可以导致手机亮灭屏的因素有多种,而在本文中主要讲解按power键亮灭屏过程以及来电亮屏.在亮灭屏过程power中主要的实现 ...

  3. Android7.0 PowerManagerService 之亮灭屏(二) PMS 电源状态管理updatePowerStateLocked()...

    本篇注意接着上篇[Android7.0 PowerManagerService 之亮灭屏(一)]继续分析量灭屏的流程,这篇主要分析PMS的状态计算和更新流程,也是PMS中最为重要和复杂的一部分电源状态 ...

  4. Android 系统(41)---Android7.0 PowerManagerService亮灭屏分析(二)

    Android7.0 PowerManagerService亮灭屏分析(二) 3029 在PowerManagerService中对各种状态进行判断后,将其数值封装进DisplayPowerReque ...

  5. Android 知识点 109 —— Android7.0 PowerManagerService 之亮灭屏

    原文地址: https://www.cnblogs.com/dyufei/p/8017604.html 写的太好了,粘过来! 本篇从按下power按键后,按键事件从InputManagerServic ...

  6. Android7.0 PowerManagerService(3) 核心函数updatePowerStateLocked的主要流程

    前面的博客中,我们已经分析过,当Android中的进程要使用电量时,需要向PMS申请WakeLock:当进程完成工作后,需要释放对应的WakeLock.  PMS收到申请和释放WakeLock的请求后 ...

  7. Android7.0 PowerManagerService(2) WakeLock的使用及流程

    作为移动终端,电量是一种稀缺资源,需要尽可能的节省.于是,Android系统在空闲时,会主动进入到休眠状态.  我们知道整个Android系统中运行着很多个进程,因此必须有一种机制能够知道每个进程是否 ...

  8. Android7.0 PowerManagerService亮灭屏分析(三)

    在前面两部分已经对绘制windows与设置设备状态进行了详细讲解. 之后接着就该对亮度值进行设置, 实现亮屏动作了. 在DisplayPowerController中的animateScreenBri ...

  9. Android7.0 PowerManagerService亮灭屏分析(一)

    绪论 可以导致手机亮灭屏的因素有多种,而在本文中主要讲解按power键亮灭屏过程以及来电亮屏.在亮灭屏过程power中主要的实现类与功能如下所述: PowerManagerService.java:以 ...

  10. Android7.0 PowerManagerService亮灭屏分析(二)

    在PowerManagerService中对各种状态进行判断后,将其数值封装进DisplayPowerRequest中传入DisplayPowerController中进一步处理.在亮屏过程中Disp ...

最新文章

  1. WinCE中命令行工具Viewbin简介(查看nk.bin中包含的文件)
  2. 小目标 | DAX高级实践-Power BI与Excel联合应用
  3. UPS不间断电源培训资料
  4. WINCE串口WriteFile阻塞问题解决方法
  5. logback配置文件详解_Log4j2使用详解
  6. oracle怎么查找最大值,oracle – 在每个分区的列中查找最大值
  7. ASP.NET 控制页和内容页中的事件
  8. 【1stopt】批处理拟合
  9. 计算机的表格如何加边框,技巧:三种快速添加Excel表格边框的方法
  10. SAR图像超分辨技术
  11. —— GPS测量原理及应用复习-1 ——
  12. 大厂面试快问快答,10分钟搞定MySQL夺命20问,你都能接住吗?
  13. 有位置,来,搬个小板凳,喝酒听故事!
  14. SpringAop两种配置:xml配置和注解方式
  15. python制作相册_《自拍教程73》Python 自动生成相册文件夹
  16. 看我拿下域控-cve2020-1472-Netlogon
  17. 证明HashSet不是线程安全
  18. DAT NAT SAN FCoE
  19. 基于python的情感分析案例-用python实现文本情感分析
  20. python用requests爬取png图片并保存到本地

热门文章

  1. 基于Qt秒表设计(Qt绘图秒表示例)
  2. 白嫖JetBrains全家桶第二波与第三波
  3. ES6 模块化【暴露、引入、引入并暴露】
  4. 鸿鹄云商平台--技术框架
  5. 本地系统盘放到服务器上,怎么把本地盘挂到云服务器
  6. 中介者(Mediator)模式实例
  7. 服务器 虚拟声卡,虚拟声卡,小编教你怎么安装虚拟声卡
  8. 新域名 @live.com 和 @windowslive.com 即将上线 (from cnbeta)
  9. OSChina 周日乱弹 ——愿你在天堂也能写代码
  10. (JZ1619)2018.07.08【2018提高组】模拟C组 0 .音乐节拍