Android7.0 PowerManagerService(4) Power按键流程
来源:互联网 发布:网游数据提取 编辑:程序博客网 时间:2024/05/22 14:34
Android7.0 PowerManagerService(4) Power按键流程
按键的处理主要由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函数。
1、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; } } } }}
2、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键长按。
3、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; ......... }}
4、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(); } }}
5、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();}
6、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(); }}
7、finishPowerKeyPress
每当处理一次完整的Power键按下、弹出操作后,interceptPowerKeyUp调用finishPowerKeyPress进行最后的状态
private void finishPowerKeyPress() { mBeganFromNonInteractive = false; mPowerKeyPressCounter = 0; if (mPowerKeyWakeLock.isHeld()) { mPowerKeyWakeLock.release(); }}
从代码可以看出,主要的工作其实就是将状态变量恢复为初始值,同时释放掉最初申请的锁。
8、总结
整个Power按键的主要处理流程如上图所示。结合手机的实际情况,整个逻辑还是非常好理解的。
文献参考:
1、 Android7.0 PowerManagerService(4) Power按键流程
http://blog.csdn.net/gaugamela/article/details/52912382
- Android7.0 PowerManagerService(4) Power按键流程
- Android7.0 PowerManagerService(4) Power按键流程
- Android7.0 PowerManagerService(4) Power按键流程
- Android7.0 PowerManagerService Power按键流程
- Android7.0 PowerManagerService(2) WakeLock的使用及流程
- Android7.0 PowerManagerService(3) 核心函数updatePowerStateLocked的主要流程
- Android7.0 PowerManagerService(2) WakeLock的使用及流程
- Android7.0 PowerManagerService(3) 核心函数updatePowerStateLocked的主要流程
- Android7.0 PowerManagerService(2) WakeLock的使用及流程
- Android7.0 PowerManagerService(3) 核心函数updatePowerStateLocked的主要流程
- Android7.0 PowerManagerService(2) WakeLock的使用及流程
- Android7.0 PowerManagerService(3) 核心函数updatePowerStateLocked的主要流程
- android7.0 电源(Power)键流程
- Android7.0 PowerManagerService(1) 启动过程
- Android7.0 PowerManagerService亮灭屏分析(一)
- Android7.0 PowerManagerService亮灭屏分析(二)
- Android7.0 PowerManagerService亮灭屏分析(三)
- Android7.0 PowerManagerService(1) 启动过程
- 第15条:使可变性最小化
- Android自定义View(一) 画线段
- javaScript学习-动态为ul添加li
- wince整体解决方案编译错误的解决办法(StdAfx.obj' not allowed with multiple source files )
- Android数据加密之Aes加密
- Android7.0 PowerManagerService(4) Power按键流程
- 第三节:SpringBoot使用properties配置文件实现多环境配置
- 解决报错:错误1130- Host xxx is not allowed to connect to this MariaDb server
- Java基础之四大内部类(结合代码理解)
- MVC框架
- Maven下安装oracle详细步骤
- Windows 2008 R2 DFS分布式文件系统配置
- 复习Java(五)猜测随机生成的数字Program
- Linux服务器上配置多个Tomcat