android 7.1 屏蔽按压两次电源键(KEYCODE_POWER)打开相机

本文档详细介绍了如何在Android 7.1系统中通过修改`PhoneWindowManager.java`和`GestureLauncherService.java`文件,来禁用通过快速按下电源键两次启动摄像头的功能。修改涉及取消调用`GestureLauncherService`的相关方法,并添加日志跟踪,确保在触发该操作后不再启动相机应用。

平台

RK3288 + Android7.1

需求

Android 支持通过快速按下电源键两次启动摄像头, 在新的需求中, 需要屏蔽此功能.

修改

frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

    private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
        // Hold a wake lock until the power key is released.
        /**省略**/
        GestureLauncherService gestureService = LocalServices.getService(
                GestureLauncherService.class);
        boolean gesturedServiceIntercepted = false;
        if (gestureService != null) {
        	//注释下面两行代码.
            //gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive,
            //        mTmpBoolean);
            if (mTmpBoolean.value && mGoingToSleep) {
                mCameraGestureTriggeredDuringGoingToSleep = true;
            }
        }
        /**省略**/     

流程

LOG


system_process D/WindowManager: ALog interceptKeyBeforeQueueing KEYCODE_POWER
system_process D/WindowManager: ALog interceptKeyBeforeQueueing KEYCODE_POWER
system_process I/PowerManagerService: Going to sleep due to power button (uid 1000)...
system_process I/PowerManagerService: Sleeping (uid 1000)...
system_process I/VrManagerService: VR mode is disallowed
system_process D/mali_winsys: EGLint new_window_surface(egl_winsys_display *, void *, EGLSurface, EGLConfig, egl_winsys_surface **, egl_color_buffer_format *, EGLBoolean) returns 0x3000
? D/PermissionCache: checking android.permission.READ_FRAME_BUFFER for uid=1000 => granted (732 us)
com.android.systemui W/FingerprintManager: isFingerprintHardwareDetected(): Service not connected!
system_process D/WindowManager: ALog interceptKeyBeforeQueueing KEYCODE_POWER
system_process I/GestureLauncherService: Power button double tap gesture detected, launching camera. Interval=220ms
system_process I/PowerManagerService: Waking up from sleep (uid 1000)...
system_process I/VrManagerService: VR mode is allowed
com.android.systemui I/KeyguardViewMediator: Camera gesture was triggered, preventing Keyguard locking.
system_process I/SensorsHal: set batch: handle = 0, period_ns = 20000000ns, timeout = 0ns
system_process D/SensorsHal: Couldn't open /dev/mma8452_daemon (No such file or directory)
system_process I/SensorsHal: MmaSensor update delay: 20ms
system_process E/SensorsHal: fail to perform GSENSOR_IOCTL_APP_SET_RATE, result = -1, error is 'Bad file descriptor'
system_process E/SensorService: sensor batch failed 0xa98df420 0 0 20000000 100000000 err=Operation not permitted
system_process I/SensorsHal: set batch: handle = 0, period_ns = 20000000ns, timeout = 0ns
system_process D/SensorsHal: Couldn't open /dev/mma8452_daemon (No such file or directory)
system_process I/SensorsHal: MmaSensor update delay: 20ms
system_process E/SensorsHal: fail to perform GSENSOR_IOCTL_APP_SET_RATE, result = -1, error is 'Bad file descriptor'
system_process E/SensorService: sensor batch failed 0xa98df420 0 0 20000000 0 err=Operation not permitted
system_process V/KeyguardServiceDelegate: onStartedWakingUp()
system_process W/VibratorService: Tried to stop vibrating but there is no vibrator device.
? D/AudioHardwareTiny: adev_set_parameters: kvpairs = screen_state=on
system_process W/VibratorService: Tried to vibrate but there is no vibrator device.
system_process D/WindowManager: ALog interceptKeyBeforeQueueing KEYCODE_POWER
system_process I/ActivityManager: START u0 {act=android.media.action.STILL_IMAGE_CAMERA flg=0x14000000 cmp=com.android.camera2/com.android.camera.CameraActivity} from uid 10018 on display 0
system_process E/libEGL: call to OpenGL ES API with no current context (logged once per thread)
system_process E/ActivityManager: applyOptionsLocked: Unknown animationType=0
system_process I/ActivityManager: Start proc 1467:com.android.camera2/u0a25 for activity com.android.camera2/com.android.camera.CameraActivity
  • 加了LOG interceptKeyBeforeQueueing KEYCODE_POWER
  • 在触发后有输出关键LOG Power button double tap gesture detected, launching camera. Interval=220ms
  • 之后启动了相机 ActivityManager: START u0 {act=android.media.action.STILL_IMAGE_CAMERA flg=0x14000000 cmp=com.android.camera2/com.android.camera.CameraActivity} from uid 10018 on display 0

frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

    /** {@inheritDoc} */
    @Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        /**省略**/
        // Handle special keys.
        switch (keyCode) {
            /**省略**/
            case KeyEvent.KEYCODE_POWER: {
                android.util.Log.d(TAG, "ALog interceptKeyBeforeQueueing KEYCODE_POWER");
                try {
                    if (mWindowManager.getDualScreenFlag()) {
                        if (mWindowManager.getSecondDisplayTaskId() != -1) {
                            break;
                        }
                    }
                } catch (RemoteException e) {

                }
                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) {
        // Hold a wake lock until the power key is released.
        /**省略**/
        GestureLauncherService gestureService = LocalServices.getService(
                GestureLauncherService.class);
        boolean gesturedServiceIntercepted = false;
        if (gestureService != null) {
            gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive,
                    mTmpBoolean);
            if (mTmpBoolean.value && mGoingToSleep) {
                mCameraGestureTriggeredDuringGoingToSleep = true;
            }
        }
        /**省略**/     

frameworks/base/services/core/java/com/android/server/GestureLauncherService.java

    public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
            MutableBoolean outLaunched) {
        boolean launched = false;
        boolean intercept = false;
        long doubleTapInterval;
        synchronized (this) {
            doubleTapInterval = event.getEventTime() - mLastPowerDown;
            if (mCameraDoubleTapPowerEnabled
                    && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
                launched = true;
                intercept = interactive;
            }
            mLastPowerDown = event.getEventTime();
        }
        if (launched) {
            Slog.i(TAG, "Power button double tap gesture detected, launching camera. Interval="
                    + doubleTapInterval + "ms");
            launched = handleCameraLaunchGesture(false /* useWakelock */,
                    StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
            if (launched) {
                MetricsLogger.action(mContext, MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE,
                        (int) doubleTapInterval);
            }
        }
        MetricsLogger.histogram(mContext, "power_double_tap_interval", (int) doubleTapInterval);
        outLaunched.value = launched;
        return intercept && launched;
    }
    /**
     * @return true if camera was launched, false otherwise.
     */
    private boolean handleCameraLaunchGesture(boolean useWakelock, int source) {
        boolean userSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
        if (!userSetupComplete) {
            if (DBG) Slog.d(TAG, String.format(
                    "userSetupComplete = %s, ignoring camera launch gesture.",
                    userSetupComplete));
            return false;
        }
        if (DBG) Slog.d(TAG, String.format(
                "userSetupComplete = %s, performing camera launch gesture.",
                userSetupComplete));

        if (useWakelock) {
            // Make sure we don't sleep too early
            mWakeLock.acquire(500L);
        }
        StatusBarManagerInternal service = LocalServices.getService(
                StatusBarManagerInternal.class);
        service.onCameraLaunchGestureDetected(source);
        return true;
    }

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java

    @Override
    public void onCameraLaunchGestureDetected(int source) {
        mLastCameraLaunchSource = source;
        if (mStartedGoingToSleep) {
            mLaunchCameraOnFinishedGoingToSleep = true;
            return;
        }
        if (!mNotificationPanel.canCameraGestureBeLaunched(
                mStatusBarKeyguardViewManager.isShowing() && mExpandedVisible)) {
            return;
        }
        if (!mDeviceInteractive) {
            PowerManager pm = mContext.getSystemService(PowerManager.class);
            pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:CAMERA_GESTURE");
            mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
        }
        vibrateForCameraGesture();
        if (!mStatusBarKeyguardViewManager.isShowing()) {
        	//启动摄像头
            startActivity(KeyguardBottomAreaView.INSECURE_CAMERA_INTENT,
                    true /* dismissShade */);
        } else {
            if (!mDeviceInteractive) {
                // Avoid flickering of the scrim when we instant launch the camera and the bouncer
                // comes on.
                mScrimController.dontAnimateBouncerChangesUntilNextFrame();
                mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
            }
            if (mScreenTurningOn || mStatusBarKeyguardViewManager.isScreenTurnedOn()) {
                mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
            } else {
                // We need to defer the camera launch until the screen comes on, since otherwise
                // we will dismiss us too early since we are waiting on an activity to be drawn and
                // incorrectly get notified because of the screen on event (which resumes and pauses
                // some activities)
                mLaunchCameraOnScreenTurningOn = true;
            }
        }
    }

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java

    public static final Intent INSECURE_CAMERA_INTENT =
            new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
Android 系统中,`KEYCODE_POWER`(电源键)是一个**系统级保留按键**,默认由系统统一处理(如点亮/熄屏、弹出关机菜单等)。如果你的应用尝试拦截它,通常会被系统阻止或仅在特定条件下生效。 但你的需求是: > ✅ **不要拦截 `KEYCODE_POWER`,而是确保不干扰系统的默认行为,把按键处理“释放”回系统或其他应用。** 这其实是很多开发者误解的反向问题 —— 大多数人想“拦截”,而你是想“**不拦截、放行**”。 --- ### ✅ 正确理解:如何“不拦截”KEYCODE_POWER? 当你 **没有主动消费(consume)** 这个事件时,它就会自然传递给下一层(比如系统),从而执行默认行为。 所以,“去掉拦截”的本质就是:**不要在任何地方 `return true` 来消费这个按键事件。** --- ### 🚫 错误做法:主动拦截电源键 ```java @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_POWER) { // ❌ 错误!这会拦截电源键 return true; // 消费了事件 → 系统收不到 → 无法锁屏! } return super.onKeyDown(keyCode, event); } ``` > ⚠️ 效果:按下电源键无反应 —— 因为你“吃掉”了事件! --- ### ✅ 正确做法:放行 KEYCODE_POWER #### 方法一:完全不管电源键(推荐) ```java @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // 只处理你关心的键 switch (keyCode) { case KeyEvent.KEYCODE_DPAD_LEFT: handleLeft(); // 自定义逻辑 return true; // 消费你关心的键 case KeyEvent.KEYCODE_DPAD_RIGHT: handleRight(); return true; // 注意:不要写 KEYCODE_POWER 的判断! } // 其他按键交给父类处理 return super.onKeyDown(keyCode, event); } ``` > ✅ 效果:`KEYCODE_POWER` 不会被你的代码处理 → 传递给系统 → 正常锁屏。 --- #### 方法二:显式调用父类,确保不拦截 ```java @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_POWER) { // 显式返回 false 或 super,表示不处理 return super.onKeyDown(keyCode, event); // ✅ 放行 } // 处理其他按键... return super.onKeyDown(keyCode, event); } ``` 或者更简单地什么都不做: ```java // 直接不重写 onKeyDown / onKeyUp 对电源键的处理 // → 默认就不会拦截 ``` --- ### 🔐 特殊情况:某些设备/ROM 允许拦截电源键? 是的,在一些定制 ROM 或 rooted 设备上,可以通过以下方式“接管”电源键: - 使用 `BroadcastReceiver` 监听 `Intent.ACTION_CLOSE_SYSTEM_DIALOGS` - 或使用 AccessibilityService - 甚至通过 `adb shell` 注入事件 但这属于**高权限操作**,普通 App 无法实现,也不推荐用于常规开发。 > ⚠️ Google Play 审核政策禁止滥用此类权限,可能导致下架。 --- ### ✅ 如何验证是否放行成功? 1. 安装你的 App。 2. 按下电源键。 3. 观察: - 屏幕是否正常熄灭? - 是否弹出关机/重启菜单(长按时)? - 是否有延迟或无响应? ✅ 如果一切正常 → 说明你**没有拦截**,系统已正确接收事件。 --- ### 💡 常见误区澄清 | 误解 | 实际情况 | |------|----------| | “我必须写代码来放行电源键” | ❌ 不需要!只要你不消费它,就自动放行 | | “重写了 onKeyDown 就能控制所有按键” | ❌ 即使你消费了,系统也可能强制干预(尤其对电源键) | | “可以监听电源键状态” | ✅ 可以通过广播 `ACTION_SCREEN_OFF` / `ACTION_SCREEN_ON` 间接感知 | --- ### ✅ 推荐:用广播监听屏幕状态变化(替代方案) 如果你想“知道”电源键被按下了,可以用以下方式监听屏幕开关: ```java IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { Log.d("PowerKey", "屏幕关闭(用户可能按了电源键)"); } else if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) { Log.d("PowerKey", "屏幕开启"); } } }; registerReceiver(receiver, filter); ``` > ✅ 合法、安全、无需特殊权限。 --- ### 总结 | 目标 | 实现方式 | |------|----------| | ✅ 不拦截 `KEYCODE_POWER` | 不要在 `onKeyDown/onKeyUp` 中 `return true` | | ✅ 让系统正常锁屏 | 避免消费电源键事件 | | ✅ 感知电源键行为 | 使用 `ACTION_SCREEN_OFF` 广播监听 | > 🔑 核心原则:**不做处理 = 自动放行** ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值