如何用厂商签名系统应用在非Root设备禁用关机重启及关机弹窗?
拦截长按电源键关机对话框的实现方案(系统签名App专属)
作为资深Android系统开发者,我很清楚你这个需求的可行性——因为你的App是厂商密钥签名的系统级应用,这就拥有了普通第三方App没有的权限和系统API访问能力,完全能实现拦截长按电源键弹出的关机对话框,甚至按需限制电源键功能。下面给你两种靠谱的实现思路:
方案一:从源头拦截电源键长按事件,直接阻止对话框弹出
这种方式是最彻底的,直接把电源键长按的动作拦截下来,不让系统触发关机对话框:
- 先声明必要的系统权限
这些权限只有系统签名的App才能申请通过,放心加在AndroidManifest.xml里:
<uses-permission android:name="android.permission.INJECT_EVENTS" /> <uses-permission android:name="android.permission.DEVICE_POWER" />
- 写个后台服务监听按键事件
实现一个后台服务,通过系统的InputManager注册输入事件监听器,专门拦截电源键的长按动作:
public class PowerKeyInterceptorService extends Service { private InputManager mInputManager; private InputEventListener mInputEventListener; @Override public void onCreate() { super.onCreate(); mInputManager = (InputManager) getSystemService(INPUT_SERVICE); mInputEventListener = new InputEventListener(); // 把监听器优先级设到最高,确保咱的代码先于系统处理事件 mInputManager.registerInputEventListener(mInputEventListener, InputManager.INPUT_EVENT_MODE_WAIT_FOR_FINISH); } private class InputEventListener implements InputManager.InputEventListener { @Override public void onInputEvent(InputEvent event) { if (event instanceof KeyEvent) { KeyEvent keyEvent = (KeyEvent) event; // 精准拦截电源键的长按事件 if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_POWER && keyEvent.getAction() == KeyEvent.ACTION_LONG_PRESS) { // 把这个事件标记为已消费,系统就不会继续处理了 event.setFlags(event.getFlags() | InputEvent.FLAG_CONSUMED); } } } } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onDestroy() { super.onDestroy(); // 记得注销监听器,避免内存泄漏 if (mInputManager != null && mInputEventListener != null) { mInputManager.unregisterInputEventListener(mInputEventListener); } } }
- 启动这个后台服务
在App启动的时候把服务拉起来,确保一直监听:
startService(new Intent(this, PowerKeyInterceptorService.class));
方案二:等对话框弹出后,直接强制关闭它
如果你不想完全拦截电源键长按,只是不想让关机对话框显示出来,也可以用这种方式:
- 声明权限并注册广播接收器
同样在AndroidManifest.xml里加权限和接收器,把优先级设到最高,确保咱的接收器先收到广播:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <receiver android:name=".ShutdownDialogReceiver" android:enabled="true" android:exported="true"> <intent-filter android:priority="1000"> <action android:name="android.intent.action.CLOSE_SYSTEM_DIALOGS" /> <action android:name="android.intent.action.ACTION_SHUTDOWN" /> </intent-filter> </receiver>
- 实现广播接收器的逻辑
收到广播后,直接通过系统窗口管理器找到关机对话框并关闭:
public class ShutdownDialogReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) || Intent.ACTION_SHUTDOWN.equals(action)) { // 作为系统App,咱能直接拿到系统窗口管理器 WindowManager windowManager = (WindowManager) context.getSystemService(WINDOW_SERVICE); // 遍历系统窗口,找到关机对话框并移除 for (WindowManager.LayoutParams params : windowManager.getWindowLayoutParams()) { if (params.type == WindowManager.LayoutParams.TYPE_SYSTEM_ALERT) { windowManager.removeViewImmediate(params.getView()); } } // 把广播拦截下来,不让系统继续处理 abortBroadcast(); } } }
几个要注意的点
- 以上方案只适用于厂商密钥签名的系统级App,普通第三方App拿不到这些权限,也碰不到系统级的事件。
- 如果想完全禁用电源键,只要在方案一里把电源键的所有事件(短按+长按)都拦截就行。
- 不同Android版本的系统窗口类型或者广播行为可能有点差异,建议针对你的目标版本做下适配测试。
内容的提问来源于stack exchange,提问作者fadedbee
相关产品推荐
相关产品推荐

