长按power键显示关机界面流程

本文详细解析了Android系统中长按电源键触发关机菜单的内部实现机制,包括事件拦截、延迟消息处理及不同行为模式的逻辑判断。深入探讨了FreemePhoneWindowManager如何重写父类方法,自定义关机界面的显示流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

FreemePhoneWindowManager.java

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
      int result = super.interceptKeyBeforeQueueing(event, policyFlags); 
      ...... 
}

PhoneWindowManager.java

    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        
        ......
        // Handle special keys.
        switch (keyCode) {
          ......

            case KeyEvent.KEYCODE_POWER: {
                // Any activity on the power button stops the accessibility shortcut
                cancelPendingAccessibilityShortcutAction();
                result &= ~ACTION_PASS_TO_USER;
                isWakeKey = false; // wake-up will be handled separately
                if (down) {
                    interceptPowerKeyDown(event, interactive);//如果按下会执行该方法
                } else {
                    interceptPowerKeyUp(event, interactive, canceled);
                }
                break;
            }

            ......
    }
    private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
            ......

        // 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
                || mA11yShortcutChordVolumeUpKeyTriggered || gesturedServiceIntercepted;
        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.
                if (hasLongPressOnPowerBehavior()) {
                    Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageDelayed(msg,
                            ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
                }//到Handler中去执行显示界面和关机的操作
            } else {
                wakeUpFromPowerKey(event.getDownTime());

                if (mSupportLongPressPowerWhenNonInteractive && hasLongPressOnPowerBehavior()) {
                    Message msg = mHandler.obtainMessage(MSG_POWER_LONG_PRESS);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageDelayed(msg,
                            ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
                    mBeganFromNonInteractive = true;
                } else {
                    final int maxCount = getMaxMultiPressPowerCount();

                    if (maxCount <= 1) {
                        mPowerKeyHandled = true;
                    } else {
                        mBeganFromNonInteractive = true;
                    }
                }
            }
        }

 

mHandler = new PolicyHandler();
    private class PolicyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                ......
                case MSG_POWER_LONG_PRESS:
                    powerLongPress();
                    break;
                ......
     
            }
        }
    }

 

    private void powerLongPress() {
        final int behavior = getResolvedLongPressOnPowerBehavior();
        switch (behavior) {
        case LONG_PRESS_POWER_NOTHING:
            break;
        case LONG_PRESS_POWER_GLOBAL_ACTIONS:
            mPowerKeyHandled = true;
            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
            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;
        }
    }

FreemePhoneWindowManager.java重写了该方法

    @Override
    protected void showGlobalActionsInternal() {
        sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
        com.freeme.server.policy.globalaction.FreemeGlobalActions
                .getInstance(mContext, mWindowManagerFuncs)
                .showDialog(isKeyguardShowingAndNotOccluded(), isDeviceProvisioned());
    }

FreemeGlobalActions.java

    public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned) {
        if (mFreemeGlobalActionsShowing) {
            return;
        }
        mKeyguardShowing = keyguardShowing;
        mIsDeviceProvisioned = isDeviceProvisioned;
        mHandler.sendEmptyMessage(MESSAGE_SHOW);
    }
    private Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                ......
                case MESSAGE_SHOW: {
                    handleShow();
                } break;
     }

 

FreemeGlobalActions.java

    private void createGlobalActionsView() {
        if (mGlobalActionsView != null) {
            mWM.removeView(mGlobalActionsView);
            mGlobalActionsView = null;
        }

        int themeID = mContext.getResources().getIdentifier("Theme.Freeme.Panel", "style", "freeme");
        ContextThemeWrapper themeContext = new ContextThemeWrapper(mContext, themeID);
        LayoutInflater inflater = LayoutInflater.from(themeContext);

        int flags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
                | WindowManager.LayoutParams.FLAG_FULLSCREEN
                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
        int stretch = -1;

        WindowManager.LayoutParams
                lp = new WindowManager.LayoutParams(stretch, stretch, WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, flags, stretch);
        /*/
        lp.privateFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
        //*/
        lp.privateFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS | WindowManager.LayoutParams.PRIVATE_FLAG_COLLAPSE_NAVI_BAR;
        //*/
        lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
        //*/ FIXME: 17-7-13  replace anim later
        lp.windowAnimations = android.R.style.Animation_Toast;
        //*/
        mGlobalActionsView = (FreemeGlobalActionsView) inflater.inflate(R.layout.globalaction_root_view_freeme, null);//mGlobalActionsView即为要显示的关机diglog,globalaction_root_view_freeme为xml视图
        mGlobalActionsView.setContentDescription(mContext.getResources().getString(R.string.globalaction_poweroff_widget_name));
        mGlobalActionsView.initUI(mHandler.getLooper());
        mGlobalActionsView.registerActionPressedCallback(this);
        mGlobalActionsView.setVisibility(View.INVISIBLE);
        mGlobalActionsView.requestFocus();
        mWM.addView(mGlobalActionsView, lp);
        mGlobalActionsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
                | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
    }

 

<think>我们正在解决如何禁用按电源触发的强制关机功能。在CentOS系统中,这通常与系统的电源管理设置有关,特别是与systemd和acpid(高级配置与电源接口守护进程)相关。 步骤分析: 1. 了解按电源的行为:在Linux系统中,按电源会触发一个硬件事件,该事件通常被acpid捕获并执行相应的动作。在默认配置下,按电源会触发强制关机(类似于断电)。 2. 我们的目标:修改配置,使得按电源不再触发强制关机,而是忽略该事件或者执行其他无害操作(如弹出关机菜单等)。但是,请注意,完全禁用这个行为可能需要修改acpid的配置或者修改systemd的配置(因为较新的系统可能使用systemd处理电源事件)。 然而,根据CentOS版本的不同,处理方式可能略有差异。在较新的CentOS版本(如7及以上)中,systemd已经接管了大部分电源事件,包括电源按钮。因此,我们需要修改systemd的logind配置。 具体步骤: 1. 修改logind.conf配置文件: systemd的logind服务负责管理用户登录和电源事件。我们可以通过修改/etc/systemd/logind.conf文件来改变电源按钮的行为。 在logind.conf中,有几个相关的选项: - HandlePowerKey: 按下电源(短按)时的行为 - HandleSuspendKey: 挂起 - HandleHibernateKey: 休眠 - HandleLidSwitch: 合上笔记本盖子的行为 - 还有一个我们关心的:HandleLongPressPowerKey: 按电源的行为 注意:HandleLongPressPowerKey选项可能在某些版本中才被引入(systemd版本>=230)。因此,我们需要先确认systemd的版本。 2. 查看systemd版本: 在终端中运行:`systemctl --version`,查看第一行,例如: systemd 239 (239-45.el8) 如果版本号大于等于230,则支持HandleLongPressPowerKey选项。 3. 如果版本支持,我们可以编辑/etc/systemd/logind.conf,设置: HandleLongPressPowerKey=ignore 也可以设置为其他动作,如: ignore: 忽略事件 poweroff: 关机(默认行为) reboot: 重启 halt: 停机(不切断电源) kexec: 通过kexec重启(跳过BIOS) suspend: 挂起 hibernate: 休眠 hybrid-sleep: 混合睡眠(挂起并休眠) lock: 锁定会话 4. 如果systemd版本较低(<230),则可能没有HandleLongPressPowerKey选项。在这种情况下,我们需要考虑使用acpid来捕获事件并忽略它。 对于acpid,我们需要修改或添加acpid的规则。通常,acpid的配置文件在/etc/acpi/events/目录下。 我们可以创建一个新的事件规则文件,例如/etc/acpi/events/powerbtn-longpress,内容如下: event=button/power.* action=/bin/true 这表示当捕获到电源按钮事件(包括按)时,执行一个什么都不做的命令(/bin/true),从而忽略事件。 但是,请注意,acpid可能已经有一个处理电源按钮事件的规则(如/etc/acpi/events/powerbtn),我们需要确保按事件不会被该规则处理,或者修改该规则。不过,在较新的系统中,acpid可能已经被配置为不处理电源按钮事件(因为由systemd处理),所以我们需要先确认。 5. 修改配置后,需要重新加载服务: 对于systemd配置的修改: sudo systemctl restart systemd-logind 对于acpid配置的修改: sudo systemctl restart acpid 6. 注意:在某些硬件上,按电源的行为可能由硬件直接处理(即硬关机),此时软件无法拦截。这种情况很少见,通常只在一些嵌入式设备上。 根据用户提供的引用,我们并没有直接相关的引用内容,但是我们可以参考CentOS系统配置的一般方法。 由于用户没有指定CentOS版本,我们假设是较新的版本(如CentOS 7或8)。在CentOS 7中,systemd的版本是219,这个版本可能不支持HandleLongPressPowerKey选项。在CentOS 8中,systemd版本为239,支持该选项。 因此,对于CentOS 7,我们可能需要使用acpid的方法。 具体操作: 情况一:系统使用systemd处理且版本>=230(如CentOS 8及以上) 1. 编辑/etc/systemd/logind.conf,找到或添加: HandleLongPressPowerKey=ignore 2. 重启systemd-logind服务: sudo systemctl restart systemd-logind 情况二:系统使用acpid处理(如CentOS 7或更低版本) 1. 确保acpid服务已安装并运行: sudo yum install acpid sudo systemctl enable acpid --now 2. 创建或编辑/etc/acpi/events/powerbtn-longpress,内容如下: event=button/power PBTN 00000080 # 注意:这个事件标识可能因硬件而异,通常按电源的事件是PBTN,但持续时间不同。更通用的做法是捕获所有电源按钮事件,然后忽略按,但这样也会忽略短按。因此,我们需要更精确的事件。 action=/bin/true 但是,如何区分短按和按?在acpid中,按电源通常会产生一个不同的事件。我们可以通过查看/var/log/messages或使用acpi_listen命令来捕获事件。 步骤: a. 打开一个终端,运行:`sudo acpi_listen` b. 按电源,观察输出。例如,可能会输出: button/power PBTN 00000080 # 按事件 c. 短按电源,可能会输出: button/power PBTN 00000080 # 注意,可能相同,也可能不同 实际上,很多硬件在按和短按时发送的信号是一样的,只是按时会持续发送多次。但有些硬件会发送不同的事件码。 如果按和短按的事件相同,那么我们不能简单地忽略所有电源按钮事件,因为那样也会禁用短按电源的功能。因此,我们需要在acpid脚本中通过计时来区分。 这比较复杂。因此,如果系统同时有systemd和acpid,并且systemd已经处理了短按事件,那么我们可以将按事件忽略,而不会影响短按。 在CentOS 7中,默认情况下,短按电源由systemd-logind处理(通过HandlePowerKey设置),而按事件可能由acpid处理?或者由systemd-logind处理?实际上,在CentOS 7中,systemd版本219不支持HandleLongPressPowerKey,所以按事件可能由acpid处理。 我们可以查看acpid的默认规则:在/etc/acpi/events/powerbtn,通常它调用一个脚本/etc/acpi/powerbtn.sh。这个脚本会检查是否有图形界面,然后执行相应的动作。但是,按事件可能被同一个规则捕获。 为了避免复杂化,我们可以尝试先设置acpid忽略按事件,但保留短按事件。然而,由于事件相同,我们无法直接区分。 因此,更可行的方法是:修改/etc/acpi/powerbtn.sh脚本,使其在检测到按事件时退出而不做任何操作,但保留短按事件的处理。但是,该脚本默认不区分按和短按。 鉴于这种情况,如果用户确实需要禁用关机,我们可以考虑完全禁用电源按钮的软件响应(包括短按和按),然后通过其他方式(如设置短按电源执行其他动作)来恢复短按功能。但这样可能不符合用户需求。 考虑到复杂性,如果用户使用的是CentOS 7,建议升级到CentOS 8或更高版本,以便使用systemd的HandleLongPressPowerKey选项。 如果用户坚持在CentOS 7上操作,我们可以尝试以下方法: 1. 编辑/etc/systemd/logind.conf,设置: HandlePowerKey=ignore # 忽略短按电源事件(这样systemd-logind就不会处理短按) 2. 然后,配置acpid来处理电源按钮事件,并在脚本中区分按和短按。 在/etc/acpi/events/powerbtn中,我们让它调用一个自定义脚本,例如/path/to/custom-powerbtn.sh。 在自定义脚本中,我们可以通过检测按钮按下的持续时间来区分。但是,acpid事件本身并不包含持续时间,因此我们需要在脚本中计时。 脚本思路: - 当收到电源按钮事件时,记录时间,并开始计时。 - 等待一段时间(比如1秒),如果在这段时间内再次收到事件(按会连续发送事件),则认为是按,忽略。 - 如果等待后没有新事件,则认为是短按,执行短按动作(如弹出关机对话框)。 但是,这种方法不可靠,因为事件发送的间隔和次数可能因硬件而异。 鉴于以上复杂性,我们建议在支持HandleLongPressPowerKey的系统(如CentOS 8及以上)上操作。 因此,针对用户的问题,我们首先推荐使用systemd的HandleLongPressPowerKey选项(如果系统支持)。如果不支持,则考虑升级系统或接受默认行为。 根据用户提供的引用,没有直接相关的解决方案,所以我们只能提供通用方法。 操作步骤总结(CentOS 8及以上): 1. 打开终端,使用root权限编辑文件:`/etc/systemd/logind.conf` sudo vi /etc/systemd/logind.conf 2. 在文件中找到或添加一行: HandleLongPressPowerKey=ignore 3. 保存文件,然后重启systemd-logind服务: sudo systemctl restart systemd-logind 4. 验证:按电源,应该不再触发关机。 注意:此设置不会影响短按电源的行为。如果需要修改短按行为,可以设置HandlePowerKey选项。 如果系统是CentOS 7(systemd版本<230),我们可以尝试以下方法: 1. 安装并启用acpid(如果尚未安装): sudo yum install acpid sudo systemctl enable acpid --now 2. 创建一个新的acpi事件规则,例如`/etc/acpi/events/powerbtn-longpress`,内容如下: event=button/power PBTN 00000080 # 这里的事件码需要根据实际捕获的事件填写,00000080可能代表按 action=/bin/true 但是,我们需要先通过`acpi_listen`获取按事件的确切字符串。 3. 重启acpid服务: sudo systemctl restart acpid 由于不同硬件事件可能不同,此方法不一定通用。因此,在CentOS 7上可能需要针对特定硬件调整。 鉴于以上原因,我们优先推荐使用支持HandleLongPressPowerKey的系统版本。 如果用户无法升级,且上述acpid方法不奏效,可能需要考虑其他安全措施(如物理上防止按电源),但这超出了软件配置的范围。 最后,请注意:修改电源按钮行为可能会带来安全风险,例如在系统无响应时无法强制关机。请谨慎操作。 根据用户提供的引用,我们无法找到直接相关的配置,因此以上方法基于Linux电源管理的通用知识。 相关问题: 1. 如何查看当前系统的systemd版本? 2. 如何通过acpid_listen捕获电源按钮事件? 3. 在CentOS 7中,如何修改短按电源的行为? 4. 修改logind.conf后,如何在不重启服务的情况下使配置生效?(注意:重启logind服务会导致当前登录的用户会话被终止,所以最好在重启服务前保存所有工作)</think>### 禁用 CentOS 按电源强制关机功能 在 CentOS 中禁用按电源的强制关机功能,需通过修改 `logind.conf` 配置文件实现。以下是具体步骤: #### 操作步骤: 1. **编辑配置文件** 打开终端,使用 root 权限编辑 `/etc/systemd/logind.conf`: ```bash sudo vi /etc/systemd/logind.conf ``` 2. **修改电源行为** 在文件中添加或修改以下参数: ```ini # 短按电源行为(可选) HandlePowerKey=ignore # 按电源行为(核心设置) HandleLongPressPowerKey=ignore ``` - `ignore` 表示忽略事件 - 其他可选值:`poweroff`(关机)、`reboot`(重启)、`hibernate`(休眠) 3. **重启 systemd-logind 服务** ```bash sudo systemctl restart systemd-logind ``` #### 验证配置: ```bash # 检查配置是否生效 cat /etc/systemd/logind.conf | grep HandleLongPressPowerKey # 应返回:HandleLongPressPowerKey=ignore ``` #### 注意事项: 1. **权限要求**:所有操作需 root 权限 2. **系统兼容性**: - `HandleLongPressPowerKey` 参数需 systemd ≥ 230 版本(CentOS 7 默认 219,需升级) - 检查 systemd 版本:`systemctl --version | head -1` 3. **硬件差异**:部分服务器硬件可能直接在 BIOS 中处理按事件,需在 BIOS 设置中禁用 > ⚠️ 警告:禁用后系统无响应时需通过物理电源按钮强制关机,可能增加数据损坏风险。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值