【PWM】PhoneWindowManager之Power键事件处理。

本文详细解释了在Android12的PhoneWindowManager(PWM)中,Power键的初始化流程以及长按事件的处理机制,包括事件的监听规则和不同行为的响应方式。

序言:

1.PhoneWindowManager简称PWM。
2.PWM有超过6千行代码,不一一讲解,将分数篇,讲解不同事件的处理。

本篇将讲解Power事件的初始化流程,以及事件拦截。

一,PhoneWindowManager 是什么?

PWM负责Window System与FW的通信,处理与设备展示相关的事件。包括窗口管理,触摸事件,点击事件,以及Power键相关的事件处理。

二,Power键的初始化流程。

  1. 初始化入口:init(Context , WindowManagerFuncs )
    /** {@inheritDoc} */
    @Override
    public void init(Context context, WindowManagerFuncs windowManagerFuncs) {
        mContext = context;
        mWindowManagerFuncs = windowManagerFuncs;
        
        // 代码省略...
        // 初始化单个按键监听(也有组合键监听,不在此篇范畴)
        initSingleKeyGestureRules();
        // 代码省略...
    }
  1. 注册单按键监听:initSingleKeyGestureRules()

    private void initSingleKeyGestureRules() {
        /**
         * 初始化单键手势监听类,这里get(Context)的实现,
         * 并非我们预想的单例模式,而是为了方便初始化一些静态变量。
         * 详情可查对应方法,这里不赘述。
         **/
        mSingleKeyGestureDetector = SingleKeyGestureDetector.get(mContext);

        int powerKeyGestures = 0;
        
        // 判断当前设备是否支持Power键超长按事件
        if (hasVeryLongPressOnPowerBehavior()) {
            powerKeyGestures |= KEY_VERYLONGPRESS;
        }
        
        // 判断当前设备是否支持Power键长按事件
        if (hasLongPressOnPowerBehavior()) {
            powerKeyGestures |= KEY_LONGPRESS;
        }
       
        // 添加Power按键监听
        mSingleKeyGestureDetector.addRule(new PowerKeyRule(powerKeyGestures));

        // 判断当前设备是否支持长按Back事件
        if (hasLongPressOnBackBehavior()) {
            // 如果支持,则添加监听。
            mSingleKeyGestureDetector.addRule(new BackKeyRule(KEY_LONGPRESS));
        }
        
        // 暂时未知这个StemPrimary是干嘛用的,先忽略
        if (hasStemPrimaryBehavior()) {
            int stemPrimaryKeyGestures = 0;
            if (hasLongPressOnStemPrimaryBehavior()) {
                stemPrimaryKeyGestures |= KEY_LONGPRESS;
            }
            mSingleKeyGestureDetector.addRule(new StemPrimaryKeyRule(stemPrimaryKeyGestures));
        }
    }
  1. 私有内部类: PowerKeyRule。 这个类,注册了多个事件监听,这里,我们仅讲解长按事件,其它的事件,原理相近,不再赘述。

    /**
     * 监听Power按键
     */
    private final class PowerKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
        PowerKeyRule(int gestures) {
            super(KEYCODE_POWER, gestures);
        }

        @Override
        int getMaxMultiPressCount() {
            return getMaxMultiPressPowerCount();
        }

        @Override
        void onPress(long downTime) {
            powerPress(downTime, 1 /*count*/,
                    mSingleKeyGestureDetector.beganFromNonInteractive());
        }

        @Override
        long getLongPressTimeoutMs() {
            if (getResolvedLongPressOnPowerBehavior() == LONG_PRESS_POWER_ASSISTANT) {
                return mLongPressOnPowerAssistantTimeoutMs;
            } else {
                return super.getLongPressTimeoutMs();
            }
        }

        // Power键长按事件
        @Override
        void onLongPress(long eventTime) {
            // 1. 判断用户当前是否有在使用设备,如果没有则返回。
            //    没有使用的情况包括:屏幕未解锁,息屏等。
            
            // 2.mSupportLongPressPowerWhenNonInteractive 为true时,
            //   就算没有在使用设备,也会触发powerLongPress(eventTime);
            if (mSingleKeyGestureDetector.beganFromNonInteractive()
                    && !mSupportLongPressPowerWhenNonInteractive) {
                Slog.v(TAG, "Not support long press power when device is not interactive.");
                return;
            }

            // 长按Power键事件处理
            powerLongPress(eventTime);
        }

        @Override
        void onVeryLongPress(long eventTime) {
            mActivityManagerInternal.prepareForPossibleShutdown();
            powerVeryLongPress();
        }

        @Override
        void onMultiPress(long downTime, int count) {
            powerPress(downTime, count, mSingleKeyGestureDetector.beganFromNonInteractive());
        }
    }
  1. 长按Power事件处理方法: powerLongPress。
  private void powerLongPress(long eventTime) {
        final int behavior = getResolvedLongPressOnPowerBehavior();
        Slog.d(TAG, "powerLongPress: eventTime=" + eventTime
                + " mLongPressOnPowerBehavior=" + mLongPressOnPowerBehavior);

        switch (behavior) {
            case LONG_PRESS_POWER_NOTHING:
                // 不处理
                break;
            case LONG_PRESS_POWER_GLOBAL_ACTIONS:
                // 显示全局菜单
                mPowerKeyHandled = true;
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
                        "Power - Long Press - Global Actions");
                showGlobalActions();
                break;
            case LONG_PRESS_POWER_SHUT_OFF:
            case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
                // 标记事件已处理
                mPowerKeyHandled = true;
                // 添加其它业务逻辑
                // 关机和震动代码
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
                        "Power - Long Press - Shut Off");
                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
                mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
                break;
            case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST:
                // 显示语音助手
                mPowerKeyHandled = true;
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
                        "Power - Long Press - Go To Voice Assist");
                // Some devices allow the voice assistant intent during setup (and use that intent
                // to launch something else, like Settings). So we explicitly allow that via the
                // config_allowStartActivityForLongPressOnPowerInSetup resource in config.xml.
                launchVoiceAssist(mAllowStartActivityForLongPressOnPowerDuringSetup);
                break;
            case LONG_PRESS_POWER_ASSISTANT:
                // 显示辅助助手
                mPowerKeyHandled = true;
                performHapticFeedback(HapticFeedbackConstants.ASSISTANT_BUTTON, false,
                        "Power - Long Press - Go To Assistant");
                final int powerKeyDeviceId = Integer.MIN_VALUE;
                launchAssistAction(null, powerKeyDeviceId, eventTime,
                        AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS);
                break;
        }
    }

如上面代码所示,如果需要,在关机前处理一些逻辑,比如,Show个Dialog或做其它业务操作,可以在这个地方,加处理逻辑。但需要注意的是,需要在onPowerKeyHandled = true; 后面添加,不然,有可能会导致interceptKeyUp的handled值不正确。

      case LONG_PRESS_POWER_SHUT_OFF:
      case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
                // 标记事件已处理
                mPowerKeyHandled = true;
                // 添加其它业务逻辑
                // 关机和震动代码
                performHapticFeedback(HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON, false,
                        "Power - Long Press - Shut Off");
                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
                mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
                break;
<think>我们正在处理关于PowerKey亮灭屏功能实现或解决方案的问题。根据提供的引用内容,我们可以总结出几个关点:1.引用[1]提到:当使用错误指纹触摸模组时,系统会被唤醒(wakeup),此时按Power不会亮屏,因为系统已经处于唤醒状态。这需要特殊处理。2.引用[2]提到:在灭屏状态下,当收到通知短信时,通过`PowerManager`和`mNotificationWakeLock.acquire()`来实现亮屏。这涉及到`NotificationPanelViewController.java`中的处理流程。3.引用[4]提供了一种通过发送特定命令(DD04XXXX)来控制亮灭屏的底层方法,其中XX代表不同的开关和状态(00点亮,01熄灭)。根据以上信息,我们可以分析PowerKey亮灭屏功能的一般实现方式。在Android系统中,PowerKey通常由底层驱动捕获,然后通过Android的输入系统上报KeyEvent,最后由WindowManagerService等系统服务处理。实现亮灭屏功能的关步骤可能包括:-在PowerKey的按处理中,根据当前系统状态(屏幕是否已经点亮)来决定是否亮屏或灭屏。-需要管理设备的唤醒锁(WakeLock),确保设备在需要时保持屏幕点亮状态。-可能涉及修改底层驱动、框架层(如WindowManagerService、PowerManagerService)以及上层应用逻辑(如通知亮屏)。解决方案建议:1.**系统层实现(Framework)**:在Android框架层,PowerKey事件的处理在`PhoneWindowManager`中。可以在此类中修改`interceptKeyBeforeQueueing`方法,根据当前屏幕状态(如是否已唤醒)来决定是否要亮屏或灭屏。```javapublicintinterceptKeyBeforeQueueing(KeyEventevent,intpolicyFlags){//...其他代码if(event.getKeyCode()==KeyEvent.KEYCODE_POWER){//检查当前屏幕状态,如果已经唤醒且屏幕关闭,则可能需要亮屏//如果屏幕已经亮着,则按下PowerKey会灭屏//根据引用[1]中的问题,需要处理在已经唤醒但屏幕未亮的状态下按Power的情况}//...其他代码}```2.**使用PowerManager控制亮灭屏**:在需要亮屏的地方(如收到通知时)获取唤醒锁(亮屏),如引用[2]所示。在灭屏时释放唤醒锁。```javaPowerManagerpowerManager=(PowerManager)getSystemService(Context.POWER_SERVICE);WakeLockwakeLock=powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,"MyApp:ScreenLock");//亮屏wakeLock.acquire();//灭屏wakeLock.release();```注意:使用唤醒锁需要相应的权限(`WAKE_LOCK`)。3.**底层控制(如引用[4])**:在需要直接控制屏幕亮灭的硬件相关场合,可以通过发送特定指令(如串口命令)给硬件。比如,命令格式为`DD04XXXX`,其中:-第一个XX:开关编号(如00代表Switch1)-第二个XX:00(亮)、01(灭)这种方法通常用于嵌入式开发或者硬件控制,需要在驱动层或HAL层实现。4.**特殊情况的处理(引用[1])**:当系统被其他事件(如错误指纹)唤醒后,按Power不亮屏的问题。这时,需要在处理Power事件时,判断系统是否已经唤醒但屏幕未亮,如果是,则手动触发亮屏流程。总结:实现PowerKey亮灭屏功能,需要综合考虑按事件处理、屏幕状态管理以及唤醒锁的使用。在Android系统中,大部分逻辑在Framework层实现,而底层硬件控制则需要与驱动程序或HAL层交互。相关问题:1.在Android系统中,PowerKey事件的处理流程是怎样的?2.如何通过WakeLock控制屏幕的亮灭?3.在系统唤醒后屏幕未亮时,按Power如何正确处理亮屏?</think> ### PowerKey亮灭屏功能实现及解决方案分析 根据您提供的技术资料和需求,以下是PowerKey亮灭屏功能的系统实现方案: #### 一、核心实现原理 1. **事件响应机制**: - 按PowerKey时触发中断,系统通过`KeyEvent.ACTION_DOWN`捕获按事件 - 通过`PowerManagerService`管理屏幕状态转换: ```java // 伪代码示例 if (isScreenOff()) { wakeUpWithReason(PowerManager.WAKE_REASON_POWER_KEY); // 亮屏 } else { goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON); // 灭屏 } ``` 2. **状态判断逻辑**: - 需处理特殊场景(如指纹唤醒时按PowerKey): ```java // 引用[1]解决方案伪代码 if (isFingerprintWakeup() && powerKeyPressed) { forceDisplayOn(); // 强制亮屏处理 } ``` #### 二、具体实现方案 1. **框架层修改(Android系统)**: - 在`PhoneWindowManager.java`中实现核心逻辑: ```java public void interceptKeyBeforeQueueing(KeyEvent event) { if (event.getKeyCode() == KeyEvent.KEYCODE_POWER) { if (mPowerManager.isScreenOn()) { sleepDevice(); // 灭屏操作 } else { wakeDevice(); // 亮屏操作 } } } ``` - 引用[2]通知亮屏关联逻辑: ```java // NotificationPanelViewController.java void updateNotificationViews() { if (isScreenOff() && hasNewNotification) { mNotificationWakeLock.acquire(); // 获取唤醒锁 } } ``` 2. **底层驱动控制**: - 引用[4]硬件控制协议: ``` DD 04 XX XX 命令结构: │ DD │ 04 │ SW编号 │ 状态 │ 00(Switch1) 00(亮)/01(灭) ``` - 通过串口/USB发送控制命令实现硬件级控制 #### 三、常见问题解决方案 1. **亮屏失效问题**: - 检查系统唤醒锁状态:`adb shell dumpsys power` - 确认`PowerManager`的`WAKE_LOCK`权限是否声明 - 排查指纹模组唤醒冲突(参考[1]特殊处理逻辑) 2. **耗电异常排查**: ```shell adb shell dumpsys battery unplug # 模拟拔电 adb shell dumpsys batterystats --checkin ``` - 重点检查`PARTIAL_WAKE_LOCK`持有者 3. **背光控制优化**: - 使用PWM调光代替简单开关 - 添加亮度渐变过渡(dimmer): ```c // 驱动层伪代码 for (int i=0; i<=MAX_BRIGHTNESS; i+=10) { set_backlight(i); msleep(5); } ``` #### 四、设计建议 1. **状态机设计**: ``` +-----------+ | SCREEN_ON|<-------+ +-----+-----+ | | PowerKey | PowerKey +-----v-----+ | | SCREEN_OFF+--------+ +-----------+ ``` 2. **硬件选型要点**(参考[3]): - 选择支持GPIO中断唤醒的电源管理IC - 推荐TI TPS65263/TPS65910系列 - 预留PWM调光控制引脚 > 注:完整实现需结合具体硬件平台(如Qualcomm/MTK)的PMIC驱动和Android HAL层适配。 [^1]: 指纹唤醒时PowerKey的特殊处理 [^2]: 通知唤醒与电源管理联动 [^3]: 硬件平台选型建议 [^4]: 底层硬件控制协议
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值