Android11 Framework添加Powerkey事件去抖

文章介绍了在Android系统中,为防止短时间内频繁触发PowerKey导致屏幕反复亮灭,采用了一个名为Debouncer的工具类来实现去抖动机制。Debouncer类检测两次事件触发的时间间隔,若小于预设值则忽略后续事件,确保每次执行完一次唤醒或休眠操作后才响应新的PowerKey事件。改动涉及PowerManagerService的内部逻辑。

    需求:短时间来回触控powerkey造成屏幕来回亮灭切换,需要去抖动,即在每次响应执行完一次休眠或者唤醒事件后,再响应powerkey事件。

1、首先在framework中做了一个去抖动的工具类:

diff --git a/api/current.txt b/api/current.txt
index 689cf4833a0..e2eab020325 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -61605,6 +61605,17 @@ package android.widget.inline {
 
 }
 
+package android.yuanfudao.util {
+
+  public class Debouncer {
+    ctor public Debouncer();
+    ctor public Debouncer(long);
+    method public boolean ignore();
+    method public void setDebounceTime(long);
+  }
+
+}
+
 package dalvik.annotation {
 
   @Deprecated @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface TestTarget {
diff --git a/core/java/android/yuanfudao/util/Debouncer.java b/core/java/android/yuanfudao/util/Debouncer.java
new file mode 100755
index 00000000000..83724e9e6d7
--- /dev/null
+++ b/core/java/android/yuanfudao/util/Debouncer.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.yuanfudao.util;
+
+import android.os.SystemClock;
+
+public class Debouncer {
+
+    private long debounceTime = 500L;
+
+    private long lastTriggerTimestamp;
+
+    public Debouncer() {}
+
+    public Debouncer(long paramLong) {
+        this.debounceTime = paramLong;
+    }
+
+    public boolean ignore() {
+        long l1 = SystemClock.elapsedRealtime();
+        long l2 = this.lastTriggerTimestamp;
+        if (l2 != 0L && l1 - l2 < this.debounceTime){
+            return true;
+        }else {
            this.lastTriggerTimestamp = l1;
+            return false;
+        }
+    }
+
+    public void setDebounceTime(long paramLong) {
+        this.debounceTime = paramLong;
+    }
+}
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 07be752e01d..e9154685b05 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -59766,6 +59766,17 @@ package android.widget.inline {
 
 }
 
+package android.yuanfudao.util {
+
+  public class Debouncer {
+    ctor public Debouncer();
+    ctor public Debouncer(long);
+    method public boolean ignore();
+    method public void setDebounceTime(long);
+  }
+
+}
+
 package dalvik.annotation {
 
   @Deprecated @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface TestTarget {

2、PowerManagerService添加相关逻辑:

--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -118,6 +118,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
+import android.yuanfudao.util.Debouncer;
 
 /**
  * The power manager service is responsible for coordinating power management
@@ -612,6 +613,8 @@ public final class PowerManagerService extends SystemService
     // Per-profile state to track when a profile should be locked.
     private final SparseArray<ProfilePowerState> mProfilePowerState = new SparseArray<>();
 
+       private Debouncer debouncer = new Debouncer(2500L);
+
     private static final class ProfilePowerState {
         // Profile user id.
         final @UserIdInt int mUserId;
@@ -1701,6 +1704,13 @@ public final class PowerManagerService extends SystemService
             return false;
         }
 
+        //Heguodong add - begin
+        if (this.debouncer.ignore()) {
+            Slog.d(TAG, "[YFD_PowerManagerService]wakeUpNoUpdateLocked,!!!rejected!!!" );
+            return false;
+        }
+        //Heguodong add - end
+
         //if (isAirplaneModeOn()) {
            // setAirplaneModeOn(false);
         //}
@@ -1763,6 +1773,13 @@ public final class PowerManagerService extends SystemService
             return false;
         }
 
+        //Heguodong add - begin
+        if (this.debouncer.ignore()) {
+            Slog.d(TAG, "[YFD_PowerManagerService]goToSleepNoUpdateLocked,!!!rejected!!!" );
+            return false;
+        }
+        //Heguodong add - end
+
         Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep");
         try {
             reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX,

增量编译验证:达到的效果是每次经历完一次完整的唤醒、休眠逻辑才会响应PowerKey事件。

Mark···

<think>嗯,用户现在想在Android 13的应用层面拦截并处理电源键事件,当系统不处理的时候。之前他们可能已经了解过系统属性控制音量键的方法,或者修改Power键功能,比如屏蔽双击打开相机。现在用户的需求更偏向于应用层如何实现拦截,而不是在系统框架层修改。 首先,我需要回忆一下Android事件分发机制。通常,按键事件会先由系统处理,比如PhoneWindowManager,然后才会传递到应用。如果系统拦截了事件,应用就无法收到。用户提到的是当系统不处理时,应用如何处理,所以可能需要找到系统不消费电源键的情况,这时候应用才能捕获到。 然后,应用层拦截按键事件的方法可能有几种:重写Activity的dispatchKeyEvent或者onKeyDown,使用KeyEvent.Callback,或者注册广播接收器。但电源键属于特殊按键,系统可能默认会处理,所以需要测试这些方法是否有效。 另外,用户可能遇到的问题是如何让应用在后台也能监听电源键,但Android的安全限制可能不允许这样做。需要检查是否有权限或者特定的API可以使用。比如,使用前台服务或者设备管理员权限,但普通应用可能无法获取这些权限。 另外,参考之前的回答,用户可能已经知道在系统层如何拦截,比如修改PhoneWindowManager的interceptKeyBeforeQueueing方法。但现在要在应用层实现,可能需要找到系统未处理时的机会。 可能还需要考虑Android版本差异,比如Android 13是否有新的API或者限制,比如后台限制更严格,或者新的权限要求。比如,从某个版本开始,普通应用无法监听电源键事件,除非有特殊权限。 另外,用户提供的引用中提到在系统层拦截电源键事件,比如在interceptPowerKeyDown中屏蔽某些功能。但应用层可能需要不同的方法,比如使用MediaSession或者监听特定的广播,但电源键的广播可能不会被发送,或者需要系统签名权限。 可能还需要考虑无障碍服务,因为无障碍服务可以监听按键事件,即使用户在后台。这可能是一个可行的方法,但需要用户授权,并且应用需要声明为无障碍服务。 总结可能的步骤:1. 在Activity中重写按键处理方法;2. 使用广播接收器监听电源键;3. 使用无障碍服务拦截按键事件。然后分析每种方法的可行性和限制,特别是在Android 13上的情况。 需要验证这些方法是否在最新版本中仍然有效,比如Android 13是否允许应用监听电源键,或者是否有新的限制。比如,某些版本开始,普通应用无法通过KeyEvent.KEYCODE_POWER来监听电源键,因为系统会优先处理。 最后,给出具体的实现步骤,并指出可能的问题和限制,比如需要用户启用无障碍服务,或者无法在屏幕关闭时接收事件,或者需要系统权限等。</think>在Android 13框架中实现应用层拦截电源键事件,需通过多层级事件处理机制实现。以下是具体实现路径与技术要点: --- ### 一、事件拦截原理(基于Android 13源码) 1. **系统层事件流** 电源键事件传递路径: ```mermaid graph TD A[InputReader] --> B(InputDispatcher) B --> C{WindowManagerService} C --> D[PhoneWindowManager.interceptKeyBeforeQueueing] D --> E[系统处理/拦截] E --> F[未拦截时传递到应用] ``` 当`PhoneWindowManager`未消费事件时,会通过`InputDispatcher`传递给焦点窗口[^1] 2. **应用层捕获窗口** 需确保应用窗口具有`FLAG_DISMISS_KEYGUARD | FLAG_SHOW_WHEN_LOCKED`标志,以在锁屏界面获取焦点: ```java getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); ``` --- ### 二、核心实现方案 #### 方案1:Activity级事件拦截(推荐) ```java public class PowerInterceptorActivity extends Activity { @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.getKeyCode() == KeyEvent.KEYCODE_POWER) { if (event.getAction() == KeyEvent.ACTION_DOWN) { // 自定义处理逻辑 handlePowerKeyDown(); return true; // 消费事件 } return true; // 阻止系统默认行为 } return super.dispatchKeyEvent(event); } private void handlePowerKeyDown() { // 实现自定义电源键逻辑 } } ``` **限制条件**: - 必须保持Activity在前台焦点状态 - 无法拦截系统已处理的电源长按操作(如关机菜单) --- #### 方案2:全局事件监控(需特殊权限) ```java public class PowerMonitorService extends Service { private MediaSession mMediaSession; @Override public void onCreate() { mMediaSession = new MediaSession(this, "PowerMonitor"); mMediaSession.setCallback(new MediaSession.Callback() { @Override public boolean onMediaButtonEvent(Intent mediaButtonIntent) { KeyEvent event = mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_POWER) { // 拦截处理 return true; } return super.onMediaButtonEvent(mediaButtonIntent); } }); mMediaSession.setActive(true); } @Override public IBinder onBind(Intent intent) { return null; } } ``` **关键配置**: ```xml <service android:name=".PowerMonitorService" android:permission="android.permission.MEDIA_CONTENT_CONTROL"> <intent-filter> <action android:name="android.intent.action.MEDIA_BUTTON"/> </intent-filter> </service> ``` **权限要求**: - 需要系统签名或`MEDIA_CONTENT_CONTROL`权限(系统级应用) --- #### 方案3:无障碍服务方案(用户授权) ```java public class PowerInterceptorService extends AccessibilityService { @Override public void onAccessibilityEvent(AccessibilityEvent event) {} @Override public void onInterrupt() {} @Override protected boolean onKeyEvent(KeyEvent event) { if (event.getKeyCode() == KeyEvent.KEYCODE_POWER) { performGlobalAction(GLOBAL_ACTION_LOCK_SCREEN); return true; // 拦截事件 } return super.onKeyEvent(event); } } ``` **配置要求**: ```xml <service android:name=".PowerInterceptorService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService"/> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/power_interceptor_config"/> </service> ``` ```xml <!-- res/xml/power_interceptor_config.xml --> <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/accessibility_desc" android:accessibilityEventTypes="typeAllMask" android:accessibilityFlags="flagRequestFilterKeyEvents" android:canRequestFilterKeyEvents="true" android:settingsActivity=".SettingsActivity"/> ``` **实现限制**: - 需要用户手动开启无障碍服务 - Android 13新增`FLAG_LIMIT_INTERACTIVE_ACTIONS`限制高频交互 --- ### 三、Android 13新增限制规避 1. **后台限制突破** 使用`ForegroundService`保持进程优先级: ```java startForeground(NOTIFICATION_ID, buildNotification()); ``` 2. **电源键长按处理** 通过反射修改系统设置(需系统权限): ```java Settings.Global.putInt(getContentResolver(), "power_key_long_press_action", POWER_BUTTON_BEHAVIOR_NOTHING); ``` 3. **输入事件验证** 添加事件来源校验防止伪造: ```java if (event.getDevice().isExternal()) { return false; // 拒绝外接设备事件 } ``` --- ### 四、调试验证方法 1. **事件监控指令**: ```bash adb shell dumpsys input | grep "Power" ``` 2. **Window焦点检测**: ```bash adb shell dumpsys window windows | grep mCurrentFocus ``` 3. **无障碍服务状态检查**: ```bash adb shell settings get secure enabled_accessibility_services ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值