Power按键流程分析

从InputManagerService分析我们知道,当按下手机的Power键之后,按键事件会传到InputDispatcher 的notifyKey 方法,然后调用到调用了NativeInputManager的interceptKeyBeforeQueueing函数,最终在这个函数中会调用到PhoneWindowManager中的interceptKeyBeforeQueueing函数,以上主要属于InputManagerService的范畴,Power按键流程我们从PhoneWindowManager 的interceptKeyBeforeQueueing 方法开始分析。

方法interceptKeyBeforeQueueing关键步骤:

第一步:初始化关键变量

[java]  view plain  copy
  1. if (!mSystemBooted) {  
  2.             return 0;  
  3.         }  
  4.   
  5.         final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;  
  6.         final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;  
  7.         final boolean canceled = event.isCanceled();  
  8.         final int keyCode = event.getKeyCode();  
  9.   
  10.         final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;  

首先判断系统是否启动完成,然后就是初始化一些关键变量

Interactive 表示屏幕是否点亮,这个变量表示PowerManagerService关于屏幕的状态

Down 表示是否是ActionDown事件

Canceled 事件是否取消

keyCode 按键编码

第二步,根据interative的状态的处理一些基本逻辑

[java]  view plain  copy
  1. if (interactive || (isInjected && !isWakeKey)) {  
  2.             //事件传递给用户  
  3.             result = ACTION_PASS_TO_USER;  
  4.             isWakeKey = false;  
  5.         } else if (!interactive && shouldDispatchInputWhenNonInteractive()) {  
  6.             result = ACTION_PASS_TO_USER;  
  7.         } else {  
  8.               
  9.             result = 0;  
  10.             if (isWakeKey && (!down || !isWakeKeyWhenScreenOff(keyCode))) {  
  11.                 isWakeKey = false;  
  12.             }  
  13.         }  
  14.         if (isValidGlobalKey(keyCode)  
  15.                 && mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) {  
  16.             if (isWakeKey) {  
  17.                 //直接唤醒系统  
  18.                 wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, " android.policy:KEY");  
  19.             }  
  20.             return result;  
  21.         }  

第三步,处理Power按键的事件

[java]  view plain  copy
  1. case KeyEvent.KEYCODE_POWER: {  
  2.                 result &= ~ACTION_PASS_TO_USER;  
  3.                 isWakeKey = false// wake-up will be handled separately  
  4.                 if (down) {  
  5.                     Power键按下  
  6.                     interceptPowerKeyDown(event, interactive);  
  7.                 } else {  
  8.                     Power键释放  
  9.                     interceptPowerKeyUp(event, interactive, canceled);  
  10.                 }  
  11.                 break;  
  12.             }  

首先是Power按键的事件的话,不再分发到应用,如果是ActionDown事件执行InterceptPowerKeyDown()方法,如果是ActionUP事件则执行InterceptPowerKeyUp()方法

下一步继续分析interceptPowerKeyDown方法

[java]  view plain  copy
  1. private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {  
  2.           
  3.         if (!mPowerKeyWakeLock.isHeld()) {  
  4.             mPowerKeyWakeLock.acquire();  
  5.         }  

获取Power按键的锁,直到释放Power键

……   

[java]  view plain  copy
  1. if (interactive && !mScreenshotChordPowerKeyTriggered  
  2.                 && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {  
  3.             mScreenshotChordPowerKeyTriggered = true;  
  4.             mScreenshotChordPowerKeyTime = event.getDownTime();  
  5.             interceptScreenshotChord();  
  6.         }  

如果当前是亮屏状态,且满足触发截屏的条件,触发截屏功能

[java]  view plain  copy
  1. TelecomManager telecomManager = getTelecommService();  
  2.         boolean hungUp = false;  
  3.         if (telecomManager != null) {  
  4.             if (telecomManager.isRinging()) {  
  5.                
  6.                 telecomManager.silenceRinger();  
  7.             } else if ((mIncallPowerBehavior  
  8.                     & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0  
  9.                     && telecomManager.isInCall() && interactive) {  
  10.     
  11.                 hungUp = telecomManager.endCall();  
  12.             }  
  13.         }  

如果有电话拨入,且电话铃声响起的话,这个时候按Power键,设置电话响铃静音

如果正在接听电话,且配置了Power键挂断电话的话,按Power按键挂断正在接听的电话

       ……

[java]  view plain  copy
  1. mPowerKeyHandled = hungUp || mScreenshotChordVolumeDownKeyTriggered  
  2.                 || mScreenshotChordVolumeUpKeyTriggered || gesturedServiceIntercepted;  

如果按Power按键执行了截图功能或者电话静音或者挂断电话等功能后,Power事件不在传递,表示已经消耗掉了Power事件 

[java]  view plain  copy
  1. if (!mPowerKeyHandled) {  

如果未消耗掉Power事件,继续执行此处逻辑

分为两种情况

1, 当前是亮屏状态

2, 当前是灭屏状态

[java]  view plain  copy
  1. if (interactive) {  
  2.                 //是否支持长按功能  
  3.                 if (hasLongPressOnPowerBehavior()) {  
  4.                     Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);  
  5.                     msg.setAsynchronous(true);  
  6.                     //发送MSG_POWER_LONG_PRESS消息,有Handler来处理  
  7.                     mHandler.sendMessageDelayed(msg,  
  8.                             ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());  
  9.                 }  
  10.             } else {  
  11.                   //当前灭屏状态,此时按Power键,先唤醒系统  
  12.                    wakeUpFromPowerKey(event.getDownTime());  
  13.                   //是否支持灭屏下的长按功能  
  14.                   if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {  
  15.                     Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);  
  16.                     msg.setAsynchronous(true);  
  17.                     mHandler.sendMessageDelayed(msg,  
  18.                             ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());  
  19.                     mBeganFromNonInteractive = true;  
  20.                 } else {  
  21.                     final int maxCount = getMaxMultiPressPowerCount();  
  22.   
  23.                     if (maxCount <= 1) {  
  24.                         mPowerKeyHandled = true;  
  25.                     } else {  
  26.                         mBeganFromNonInteractive = true;  
  27.                     }  
  28.                 }  
  29.             }  
  30.         }  
  31.     }  

当前亮屏状态

首先判断是否支持长按的行为

先来看看hasLongPressOnPowerBehavior函数

[java]  view plain  copy
  1. private boolean hasLongPressOnPowerBehavior() {  
  2.         return getResolvedLongPressOnPowerBehavior() != LONG_PRESS_POWER_NOTHING;  
  3.     }  

再来看看getResolvedLongPressOnPowerBehavior函数

[java]  view plain  copy
  1. private int getResolvedLongPressOnPowerBehavior() {  
  2.         if (FactoryTest.isLongPressOnPowerOffEnabled()) {  
  3.             return LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;  
  4.         }  
  5.         return mLongPressOnPowerBehavior;  
  6. }  

由于systemProperty中的factory.long_press_power_off" 配置为0,所以不支持灭屏长按功能,。因此只要看mLongPressOnPowerBehavior这个变量

mLongPreesOnPowerBehavior 默认配置获取为

[java]  view plain  copy
  1. mLongPressOnPowerBehavior = mContext.getResources().getInteger(  
  2.                com.android.internal.R.integer.config_longPressOnPowerBehavior);  

config_longPressOnPowerBehavior 默认值为1,所以 mLongPressOnPowerBehavior  = 1, 最后hasLongPressOnPowerBehavior返回 true


[java]  view plain  copy
  1. if (hasLongPressOnPowerBehavior()) {  
  2.                     Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);  
  3.                     msg.setAsynchronous(true);  
  4.                     mHandler.sendMessageDelayed(msg,  
  5.                             ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());  
  6.                 }  

hasLongPressOnPowerBehavior返回true,则发送MSG_POWER_LONG_PRESS的Handler消息,Handler处理消息调用 powerLongPress()方法,接着我们来看下Handler处理消息的逻辑

[java]  view plain  copy
  1. private void powerLongPress() {  
  2.         final int behavior = getResolvedLongPressOnPowerBehavior();  
  3.         switch (behavior) {  
  4.         case LONG_PRESS_POWER_NOTHING:  
  5.             break;  
  6.         case LONG_PRESS_POWER_GLOBAL_ACTIONS:  
  7.             mPowerKeyHandled = true;  
  8.             if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {  
  9.                 performAuditoryFeedbackForAccessibilityIfNeed();  
  10.             }  
  11.             showGlobalActionsInternal();  
  12.             break;  
  13.        ……  

由以上的分析指导getResolvedLongPressOnPowerBehavior的值为1,所以执行case LONG_PRESS_POWER_GLOBAL_ACTIONS,

mPowerKeyHandled的值设置为true,表示处理了PowerDown事件,showGlobalActionsInternal()方法弹出关机,重启对话框

1. 当屏幕是灭屏状态

首先执行wakeUpFromPowerKey方法,唤醒手机系统,最终调用mPowerManager.wakeUp(wakeTimereason)来唤醒手机

然后mSupportLongPressPowerWhenNonInteractive默认不支持,所以默认执行

[java]  view plain  copy
  1. final int maxCount = getMaxMultiPressPowerCount();  
  2.               if (maxCount <= 1) {  
  3.                     mPowerKeyHandled = true;  
  4.                } else {  
  5.                     mBeganFromNonInteractive = true;  
  6.                }  

[java]  view plain  copy
  1. getMaxMultiPressPowerCount() ==1,默认不支持对此按键,所以将mPowerKeyHandled设置为true,表示处理了PowerDown事件  
  2. 2,处理PowerUp事件  
  3. PowerUp事件也分为两种情况,分别对应PowerDown的亮屏和灭屏两种情况  

[java]  view plain  copy
  1. private void interceptPowerKeyUp(KeyEvent event, boolean interactive, boolean canceled) {  
  2.         final boolean handled = canceled || mPowerKeyHandled;  
  3.       ……  
  4.         if (!handled) {  
  5.             mPowerKeyPressCounter += 1;  
  6.             final int maxCount = getMaxMultiPressPowerCount();  
  7.             final long eventTime = event.getDownTime();  
  8.             if (mPowerKeyPressCounter < maxCount) {  
  9.                 Message msg = mHandler.obtainMessage(MSG_POWER_DELAYED_PRESS,  
  10.                         interactive ? 1 : 0, mPowerKeyPressCounter, eventTime);  
  11.                 msg.setAsynchronous(true);  
  12.                 mHandler.sendMessageDelayed(msg, ViewConfiguration.getDoubleTapTimeout());  
  13.                 return;  
  14.             }  
  15.             powerPress(eventTime, interactive, mPowerKeyPressCounter);  
  16.         }  
  17.         finishPowerKeyPress();  
  18.     }  

在灭屏的时候,和亮屏长按的时候mPowerKeyHandled最后设置为true,即handle为true,直接执行finishPowerKeyPress方法,释放mPowerKeyWakeLock

在亮屏短按的时候,handled为false,由以上分析,手机默认不支持多次按键所以maxCount为1,mPowerKeyPressCounter=1,所以直接执行powerPress方法

[java]  view plain  copy
  1. private void powerPress(long eventTime, boolean interactive, int count) {  
  2.           
  3.         if (count == 2) {  
  4.             powerMultiPressAction(eventTime, interactive,mDoublePressOnPowerBehavior);  
  5.         } else if (count == 3) {  
  6.             powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);  
  7.         } else if (interactive && !mBeganFromNonInteractive) {  
  8.             switch (mShortPressOnPowerBehavior) {  
  9.                 case SHORT_PRESS_POWER_NOTHING:  
  10.                     break;  
  11.                 case SHORT_PRESS_POWER_GO_TO_SLEEP:  
  12.                     mPowerManager.goToSleep(eventTime,  
  13.                             PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);  
  14.                     break;  
  15.                 case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:  
  16.                     mPowerManager.goToSleep(eventTime,  
  17.                             PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,  
  18.                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);  
  19.                     break;  
  20.                 case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:  
  21.                     mPowerManager.goToSleep(eventTime,  
  22.                             PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,  
  23.                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);  
  24.                     launchHomeFromHotKey();  
  25.                     break;  
  26.                 case SHORT_PRESS_POWER_GO_HOME:  
  27.                     launchHomeFromHotKey(true /* awakenFromDreams */false   
  28.                     break;  
  29.             }  
  30.         }  
  31.     }  

由于mPowerKeyPressCounter 为1,所以执行case  SHORT_PRESS_POWER_GO_TO_SLEEP,调用mPowerManager.goToSleep休眠系统

 

Power按键流程总结

Power按键

1.亮屏 down 

           长按 弹出关闭重启对话框

           短按 不做处理

   亮屏 up 

       长按 不做处理

        短按 调用goToSleep休眠系统

2. 灭屏 down

            调用wakeup 唤醒系统

     灭屏 up

            不做处理


原文地址: http://blog.youkuaiyun.com/zhenjie_chang/article/details/52512823

<think>我们正在分析Android系统中Power控制屏幕熄灭的代码流程。根据引用内容,我们可以知道在Android 7.0中,当按下Power时,系统会触发一系列事件,最终可能导致屏幕熄灭(即进入睡眠状态)。 根据引用[2]中的描述,当设备处于亮屏状态(interactive为true)且不是从非交互状态开始时,按下Power(短按)会进入一个分支处理。在`powerPress`函数中,根据`mShortPressOnPowerBehavior`的值执行不同的操作。其中,`SHORT_PRESS_POWER_GO_TO_SLEEP`表示短按Power会使设备进入睡眠状态。 具体流程如下: 1. 当用户按下Power时,硬件产生一个按键事件,该事件被InputReader捕获,然后传递给InputDispatcher,最终到达WindowManagerService(WMS)的`PhoneWindowManager`。 2. 在`PhoneWindowManager`中,会处理Power的按下事件。在亮屏状态下,短按Power会触发`powerPress`函数。 3. 在`powerPress`函数中,当满足条件(count=1,设备处于交互状态等)时,根据配置的`mShortPressOnPowerBehavior`值执行相应操作。如果配置为`SHORT_PRESS_POWER_GO_TO_SLEEP`(值为1),则调用`PowerManagerService`的`goToSleep`方法。 4. `PowerManagerService`的`goToSleep`方法会进一步调用到底层的`goToSleepInternal`方法,该方法会请求系统进入睡眠状态。 5. 在`goToSleepInternal`中,会更新电源状态,并调用`nativeSetPowerState`(通过JNI)通知底层进行睡眠操作。同时,它会释放唤醒锁,允许系统进入低功耗状态。 6. 底层(包括HAL层和Kernel)接收到睡眠请求后,会关闭屏幕背光,并可能将CPU置于休眠状态。 值得注意的是,在进入睡眠之前,系统会保存状态,并通知其他服务(如DisplayManagerService)屏幕即将关闭。 下面是一个简化的代码流程: ``` 用户按下Power → InputReader捕获事件 → InputDispatcher分发事件到WindowManagerService → PhoneWindowManager.interceptKeyBeforeQueueing() 处理按键事件,判断为短按Power → 调用PowerManagerService的powerPress()方法 在powerPress()中,根据条件调用goToSleep() → PowerManagerService.goToSleep() 内部调用goToSleepInternal() → 在goToSleepInternal()中,更新全局状态,通过native方法调用底层 → 底层(HAL和Kernel)执行关闭屏幕和进入睡眠的操作 ``` 另外,根据引用[3],屏幕熄灭过程涉及释放唤醒锁(wake lock)和调用kernel的睡眠流程。 注意:以上分析基于Android 7.0的代码,不同版本可能有所差异。 相关问题: 1. 在Android系统中,除了Power,还有其他方式可以使屏幕熄灭吗? 2. 如何配置短按Power的行为(例如修改为锁屏而不是睡眠)? 3. Power长按事件的流程是怎样的?</think>### Power控制屏幕熄灭功能的代码流程分析Android系统中,Power控制屏幕熄灭的流程涉及多个系统层的协作。以下是基于Android 7.0源码的分析流程在不同版本中可能略有差异): #### 1. **Input事件捕获** 当用户按下Power时: - Linux内核通过`input`子系统捕获硬件中断 - InputReader线程读取`/dev/input/eventX`设备事件 - 事件被传递到WindowManagerService(WMS)的`InputManager`模块 #### 2. **WindowManagerService处理** 在`PhoneWindowManager.java`中处理按键事件: ```java // frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java public int interceptKeyBeforeQueueing(...) { if (keyCode == KeyEvent.KEYCODE_POWER) { if (down) { // 按下事件 powerPress(eventTime, interactive, mPowerKeyWakeLock); } else { // 释放事件 powerRelease(); } } } ``` #### 3. **短按Power处理** 在`powerPress()`方法中判断短按行为: ```java private void powerPress(long eventTime, boolean interactive, int count) { if (interactive) { switch (mShortPressOnPowerBehavior) { case SHORT_PRESS_POWER_GO_TO_SLEEP: // 配置值为1 mPowerManager.goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0); break; // 其他case处理... } } } ``` [^2] #### 4. **PowerManagerService执行睡眠** 在`PowerManagerService.java`中执行睡眠操作: ```java // frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java @Override public void goToSleep(long eventTime, int reason, int flags) { goToSleepInternal(eventTime, reason, flags, Process.SYSTEM_UID); } private void goToSleepInternal(...) { // 1. 更新唤醒状态 setWakefulnessLocked(WAKEFULNESS_ASLEEP, reason); // 2. 通知DisplayManagerService关闭屏幕 mDisplayManagerInternal.setDisplayPowerState(...); // 3. 释放唤醒锁 updatePowerStateLocked(); } ``` #### 5. **底层驱动执行** 最终通过HAL层和内核关闭屏幕: 1. 通过JNI调用`com_android_server_power_PowerManagerService.cpp` 2. 写入`/sys/power/state`触发内核睡眠流程 3. 显示驱动关闭背光并进入低功耗模式 ```cpp // 内核电源管理 static int suspend_enter(suspend_state_t state) { // 关闭显示控制器 fb_suspend(); // 进入CPU休眠状态 cpu_suspend(); } ``` [^3] ### 关配置点 在`config.xml`中定义Power行为: ```xml <!-- 短按行为:0=无操作 1=睡眠 2=挂断电话 3=锁屏 --> <integer name="config_shortPressOnPowerBehavior">1</integer> ``` ### 流程总结 ``` 硬件中断 → Input子系统 → WindowManagerService → PhoneWindowManager.powerPress() → PowerManagerService.goToSleep() → DisplayManagerService → SurfaceFlinger → Framebuffer驱动 → 内核电源管理(suspend) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值