当前位置:   article > 正文

InputManagerService实体按键及组合按键-Android12_android 实体按键流程

android 实体按键流程

InputManagerService实体按键及组合按键-Android12

android12-release

InputManagerService启动-Android12
InputReader线程获取输入事件-Android12
InputDispatcher线程分发事件-Android12
InputChannel通道建立-Android12
InputChannel发送Input给App-Android12


1. 手机实体按键

一般手机侧边有三个实体按键power开关机键、音量上键、音量下键:

  • KEYCODE_POWER = 26
    对应scancode码#define KEY_POWER 116
  • KEYCODE_VOLUME_UP = 24
    对应scancode码#define KEY_VOLUMEUP 115
  • KEYCODE_VOLUME_DOWN = 25
    对应scancode码#define KEY_VOLUMEDOWN 114

1.1 实体按键添加

IMS:EventHub 设备添加和InputDevice转化 手机实体按键其实也是添加设备。其中关键方法EventHub::openDeviceLocked()InputReader::addDeviceLocked()

ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=%s, "
      "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ",
      deviceId, fd, devicePath.c_str(), device->identifier.name.c_str(),
      device->classes.string().c_str(), device->configurationFile.c_str(),
      device->keyMap.keyLayoutFile.c_str(), device->keyMap.keyCharacterMapFile.c_str(),
      toString(mBuiltInKeyboardId == deviceId));

ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=0x%08x",
              device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str(),
              device->getSources());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

1.2 从dumpsys input信息查看

下面是cepheus_input.txt(小米9机器),查看下面两个dump()信息:
frameworks/native/services/inputflinger/reader/InputReader.cpp中InputReader::dump()
frameworks/native/services/inputflinger/reader/EventHub.cpp中EventHub::dump()
frameworks/native/services/inputflinger/reader/InputDevice.cpp中InputDevice::dump()
frameworks/native/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp中KeyboardInputMapper::dump()

在这里插入图片描述 在这里插入图片描述

Classes: 0x00000001 设备表示Classes码
Path: /dev/input/event0 添加的设备节点
KeyLayoutFile: /system/usr/keylayout/Generic.kl 按键布局kl文件用来完成映射过程
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm kcm文件转化为显示在文本框的字符
Sources: 0x00000101 与Classes码对应的Sources码
Keyboard Input Mapper: 添加对应InputMapper文件:frameworks/native/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp

2. 实体按键功能

具体功能实现位置在 IMS:Input事件可拦截位置

  • 添加队列之前拦截InputReader::loopOnce() -> EventHub::getEvents() -> InputReader::processEventsLocked()/InputReader::processEventsForDeviceLocked() -> InputDevice::process() -> KeyboardInputMapper::process()处理 -> QueuedInputListener::flush() -> NotifyKeyArgs::notify() -> InputDispatcher::notifyKey() -> mPolicy->interceptKeyBeforeQueueing() -> NativeInputManager::interceptKeyBeforeQueueing()/InputManagerService.java#interceptKeyBeforeQueueing() -> InputManagerCallback.java#interceptKeyBeforeQueueing() -> WindowManagerService.java#mPolicy.interceptKeyBeforeQueueing() -> PhoneWindowManager.java#interceptKeyBeforeQueueing()
  • 发送之前拦截mPolicy->interceptKeyBeforeDispatching() ===> PhoneWindowManager.java#interceptKeyBeforeDispatching()

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

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
    final int keyCode = event.getKeyCode();
    final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
    boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
            || event.isWakeKey();

    if (!mSystemBooted) {
        // If we have not yet booted, don't let key events do anything.
        // Exception: Wake and power key events are forwarded to PowerManager to allow it to
        // wake from quiescent mode during boot.
        if (down && (keyCode == KeyEvent.KEYCODE_POWER
                || keyCode == KeyEvent.KEYCODE_TV_POWER)) {
            wakeUpFromPowerKey(event.getDownTime());
        } else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP)
                && isWakeKeyWhenScreenOff(keyCode)) {
            wakeUpFromWakeKey(event);
        }
        return 0;
    }

    final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
    final boolean canceled = event.isCanceled();
    final int displayId = event.getDisplayId();
    final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;

    if (DEBUG_INPUT) {
        // If screen is off then we treat the case where the keyguard is open but hidden
        // the same as if it were open and in front.
        // This will prevent any keys other than the power button from waking the screen
        // when the keyguard is hidden by another activity.
        final boolean keyguardActive = (mKeyguardDelegate != null
                && (interactive ? isKeyguardShowingAndNotOccluded() :
                mKeyguardDelegate.isShowing()));
        Log.d(TAG, "interceptKeyTq keycode=" + keyCode
                + " interactive=" + interactive + " keyguardActive=" + keyguardActive
                + " policyFlags=" + Integer.toHexString(policyFlags));
    }

    // Basic policy based on interactive state.
    int result;
    if (interactive || (isInjected && !isWakeKey)) {
        // When the device is interactive or the key is injected pass the
        // key to the application.
        result = ACTION_PASS_TO_USER;
        isWakeKey = false;

        if (interactive) {
            // If the screen is awake, but the button pressed was the one that woke the device
            // then don't pass it to the application
            if (keyCode == mPendingWakeKey && !down) {
                result = 0;
            }
            // Reset the pending key
            mPendingWakeKey = PENDING_KEY_NULL;
        }
    } else if (shouldDispatchInputWhenNonInteractive(displayId, keyCode)) {
        // If we're currently dozing with the screen on and the keyguard showing, pass the key
        // to the application but preserve its wake key status to make sure we still move
        // from dozing to fully interactive if we would normally go from off to fully
        // interactive.
        result = ACTION_PASS_TO_USER;
        // Since we're dispatching the input, reset the pending key
        mPendingWakeKey = PENDING_KEY_NULL;
    } else {
        // When the screen is off and the key is not injected, determine whether
        // to wake the device but don't pass the key to the application.
        result = 0;
        if (isWakeKey && (!down || !isWakeKeyWhenScreenOff(keyCode))) {
            isWakeKey = false;
        }
        // Cache the wake key on down event so we can also avoid sending the up event to the app
        if (isWakeKey && down) {
            mPendingWakeKey = keyCode;
        }
    }

    // If the key would be handled globally, just return the result, don't worry about special
    // key processing.
    if (isValidGlobalKey(keyCode)
            && mGlobalKeyManager.shouldHandleGlobalKey(keyCode)) {
        // Dispatch if global key defined dispatchWhenNonInteractive.
        if (!interactive && isWakeKey && down
                && mGlobalKeyManager.shouldDispatchFromNonInteractive(keyCode)) {
            mGlobalKeyManager.setBeganFromNonInteractive();
            result = ACTION_PASS_TO_USER;
            // Since we're dispatching the input, reset the pending key
            mPendingWakeKey = PENDING_KEY_NULL;
        }

        if (isWakeKey) {
            wakeUpFromWakeKey(event);
        }
        return result;
    }

    // Alternate TV power to power key for Android TV device.
    final HdmiControlManager hdmiControlManager = getHdmiControlManager();
    if (keyCode == KeyEvent.KEYCODE_TV_POWER && mHasFeatureLeanback
            && (hdmiControlManager == null || !hdmiControlManager.shouldHandleTvPowerKey())) {
        event = KeyEvent.obtain(
                event.getDownTime(), event.getEventTime(),
                event.getAction(), KeyEvent.KEYCODE_POWER,
                event.getRepeatCount(), event.getMetaState(),
                event.getDeviceId(), event.getScanCode(),
                event.getFlags(), event.getSource(), event.getDisplayId(), null);
        return interceptKeyBeforeQueueing(event, policyFlags);
    }

    // This could prevent some wrong state in multi-displays environment,
    // the default display may turned off but interactive is true.
    final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
    final boolean interactiveAndOn = interactive && isDefaultDisplayOn;
    if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
        handleKeyGesture(event, interactiveAndOn);
    }

    // Enable haptics if down and virtual key without multiple repetitions. If this is a hard
    // virtual key such as a navigation bar button, only vibrate if flag is enabled.
    final boolean isNavBarVirtKey = ((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0);
    boolean useHapticFeedback = down
            && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0
            && (!isNavBarVirtKey || mNavBarVirtualKeyHapticFeedbackEnabled)
            && event.getRepeatCount() == 0;

    // Handle special keys.
    switch (keyCode) {
        case KeyEvent.KEYCODE_BACK: {
            if (down) {
                mBackKeyHandled = false;
            } else {
                if (!hasLongPressOnBackBehavior()) {
                    mBackKeyHandled |= backKeyPress();
                }
                // Don't pass back press to app if we've already handled it via long press
                if (mBackKeyHandled) {
                    result &= ~ACTION_PASS_TO_USER;
                }
            }
            break;
        }

        case KeyEvent.KEYCODE_VOLUME_DOWN:
        case KeyEvent.KEYCODE_VOLUME_UP:
        case KeyEvent.KEYCODE_VOLUME_MUTE: {
            if (down) {
                sendSystemKeyToStatusBarAsync(event.getKeyCode());

                NotificationManager nm = getNotificationService();
                if (nm != null && !mHandleVolumeKeysInWM) {
                    nm.silenceNotificationSound();
                }

                TelecomManager telecomManager = getTelecommService();
                if (telecomManager != null && !mHandleVolumeKeysInWM) {
                    // When {@link #mHandleVolumeKeysInWM} is set, volume key events
                    // should be dispatched to WM.
                    if (telecomManager.isRinging()) {
                        // If an incoming call is ringing, either VOLUME key means
                        // "silence ringer".  We handle these keys here, rather than
                        // in the InCallScreen, to make sure we'll respond to them
                        // even if the InCallScreen hasn't come to the foreground yet.
                        // Look for the DOWN event here, to agree with the "fallback"
                        // behavior in the InCallScreen.
                        Log.i(TAG, "interceptKeyBeforeQueueing:"
                              + " VOLUME key-down while ringing: Silence ringer!");

                        // Silence the ringer.  (It's safe to call this
                        // even if the ringer has already been silenced.)
                        telecomManager.silenceRinger();

                        // And *don't* pass this key thru to the current activity
                        // (which is probably the InCallScreen.)
                        result &= ~ACTION_PASS_TO_USER;
                        break;
                    }
                }
                int audioMode = AudioManager.MODE_NORMAL;
                try {
                    audioMode = getAudioService().getMode();
                } catch (Exception e) {
                    Log.e(TAG, "Error getting AudioService in interceptKeyBeforeQueueing.", e);
                }
                boolean isInCall = (telecomManager != null && telecomManager.isInCall()) ||
                        audioMode == AudioManager.MODE_IN_COMMUNICATION;
                if (isInCall && (result & ACTION_PASS_TO_USER) == 0) {
                    // If we are in call but we decided not to pass the key to
                    // the application, just pass it to the session service.
                    MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
                            event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
                    break;
                }
            }
            if (mUseTvRouting || mHandleVolumeKeysInWM) {
                // Defer special key handlings to
                // {@link interceptKeyBeforeDispatching()}.
                result |= ACTION_PASS_TO_USER;
            } else if ((result & ACTION_PASS_TO_USER) == 0) {
                // If we aren't passing to the user and no one else
                // handled it send it to the session manager to
                // figure out.
                MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
                        event, AudioManager.USE_DEFAULT_STREAM_TYPE, true);
            }
            break;
        }

        case KeyEvent.KEYCODE_ENDCALL: {
            result &= ~ACTION_PASS_TO_USER;
            if (down) {
                TelecomManager telecomManager = getTelecommService();
                boolean hungUp = false;
                if (telecomManager != null) {
                    hungUp = telecomManager.endCall();
                }
                if (interactive && !hungUp) {
                    mEndCallKeyHandled = false;
                    mHandler.postDelayed(mEndCallLongPress,
                            ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
                } else {
                    mEndCallKeyHandled = true;
                }
            } else {
                if (!mEndCallKeyHandled) {
                    mHandler.removeCallbacks(mEndCallLongPress);
                    if (!canceled) {
                        if ((mEndcallBehavior
                                & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) {
                            if (goHome()) {
                                break;
                            }
                        }
                        if ((mEndcallBehavior
                                & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
                            sleepDefaultDisplay(event.getEventTime(),
                                    PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                            isWakeKey = false;
                        }
                    }
                }
            }
            break;
        }

        case KeyEvent.KEYCODE_TV_POWER: {
            result &= ~ACTION_PASS_TO_USER;
            isWakeKey = false; // wake-up will be handled separately
            if (down && hdmiControlManager != null) {
                hdmiControlManager.toggleAndFollowTvPower();
            }
            break;
        }

        case KeyEvent.KEYCODE_POWER: {
            EventLogTags.writeInterceptPower(
                    KeyEvent.actionToString(event.getAction()),
                    mPowerKeyHandled ? 1 : 0,
                    mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));
            // Any activity on the power button stops the accessibility shortcut
            result &= ~ACTION_PASS_TO_USER;
            isWakeKey = false; // wake-up will be handled separately
            if (down) {
                interceptPowerKeyDown(event, interactiveAndOn);
            } else {
                interceptPowerKeyUp(event, canceled);
            }
            break;
        }

        case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN:
            // fall through
        case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP:
            // fall through
        case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT:
            // fall through
        case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT: {
            result &= ~ACTION_PASS_TO_USER;
            interceptSystemNavigationKey(event);
            break;
        }

        case KeyEvent.KEYCODE_SLEEP: {
            result &= ~ACTION_PASS_TO_USER;
            isWakeKey = false;
            if (!mPowerManager.isInteractive()) {
                useHapticFeedback = false; // suppress feedback if already non-interactive
            }
            if (down) {
                sleepPress();
            } else {
                sleepRelease(event.getEventTime());
            }
            break;
        }

        case KeyEvent.KEYCODE_SOFT_SLEEP: {
            result &= ~ACTION_PASS_TO_USER;
            isWakeKey = false;
            if (!down) {
                mPowerManagerInternal.setUserInactiveOverrideFromWindowManager();
            }
            break;
        }

        case KeyEvent.KEYCODE_WAKEUP: {
            result &= ~ACTION_PASS_TO_USER;
            isWakeKey = true;
            break;
        }

        case KeyEvent.KEYCODE_MEDIA_PLAY:
        case KeyEvent.KEYCODE_MEDIA_PAUSE:
        case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
        case KeyEvent.KEYCODE_HEADSETHOOK:
        case KeyEvent.KEYCODE_MUTE:
        case KeyEvent.KEYCODE_MEDIA_STOP:
        case KeyEvent.KEYCODE_MEDIA_NEXT:
        case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
        case KeyEvent.KEYCODE_MEDIA_REWIND:
        case KeyEvent.KEYCODE_MEDIA_RECORD:
        case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
        case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
            if (MediaSessionLegacyHelper.getHelper(mContext).isGlobalPriorityActive()) {
                // If the global session is active pass all media keys to it
                // instead of the active window.
                result &= ~ACTION_PASS_TO_USER;
            }
            if ((result & ACTION_PASS_TO_USER) == 0) {
                // Only do this if we would otherwise not pass it to the user. In that
                // case, the PhoneWindow class will do the same thing, except it will
                // only do it if the showing app doesn't process the key on its own.
                // Note that we need to make a copy of the key event here because the
                // original key event will be recycled when we return.
                mBroadcastWakeLock.acquire();
                Message msg = mHandler.obtainMessage(MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK,
                        new KeyEvent(event));
                msg.setAsynchronous(true);
                msg.sendToTarget();
            }
            break;
        }

        case KeyEvent.KEYCODE_CALL: {
            if (down) {
                TelecomManager telecomManager = getTelecommService();
                if (telecomManager != null) {
                    if (telecomManager.isRinging()) {
                        Log.i(TAG, "interceptKeyBeforeQueueing:"
                              + " CALL key-down while ringing: Answer the call!");
                        telecomManager.acceptRingingCall();

                        // And *don't* pass this key thru to the current activity
                        // (which is presumably the InCallScreen.)
                        result &= ~ACTION_PASS_TO_USER;
                    }
                }
            }
            break;
        }
        case KeyEvent.KEYCODE_ASSIST: {
            final boolean longPressed = event.getRepeatCount() > 0;
            if (down && !longPressed) {
                Message msg = mHandler.obtainMessage(MSG_LAUNCH_ASSIST, event.getDeviceId(),
                        0 /* unused */, event.getEventTime() /* eventTime */);
                msg.setAsynchronous(true);
                msg.sendToTarget();
            }
            result &= ~ACTION_PASS_TO_USER;
            break;
        }
        case KeyEvent.KEYCODE_VOICE_ASSIST: {
            if (!down) {
                mBroadcastWakeLock.acquire();
                Message msg = mHandler.obtainMessage(MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK);
                msg.setAsynchronous(true);
                msg.sendToTarget();
            }
            result &= ~ACTION_PASS_TO_USER;
            break;
        }
        case KeyEvent.KEYCODE_WINDOW: {
            if (mShortPressOnWindowBehavior == SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE) {
                if (mPictureInPictureVisible) {
                    // Consumes the key only if picture-in-picture is visible to show
                    // picture-in-picture control menu. This gives a chance to the foreground
                    // activity to customize PIP key behavior.
                    if (!down) {
                        showPictureInPictureMenu(event);
                    }
                    result &= ~ACTION_PASS_TO_USER;
                }
            }
            break;
        }
    }

    // Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users.
    if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(isKeyguardLocked())) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_Z: {
                if (down && event.isCtrlPressed() && event.isAltPressed()) {
                    mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT));
                    result &= ~ACTION_PASS_TO_USER;
                }
                break;
            }
        }
    }

    if (useHapticFeedback) {
        performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
                "Virtual Key - Press");
    }

    if (isWakeKey) {
        wakeUpFromWakeKey(event);
    }

    if ((result & ACTION_PASS_TO_USER) != 0) {
        // If the key event is targeted to a specific display, then the user is interacting with
        // that display. Therefore, give focus to the display that the user is interacting with.
        if (!mPerDisplayFocusEnabled
                && displayId != INVALID_DISPLAY && displayId != mTopFocusedDisplayId) {
            // An event is targeting a non-focused display. Move the display to top so that
            // it can become the focused display to interact with the user.
            // This should be done asynchronously, once the focus logic is fully moved to input
            // from windowmanager. Currently, we need to ensure the setInputWindows completes,
            // which would force the focus event to be queued before the current key event.
            // TODO(b/70668286): post call to 'moveDisplayToTop' to mHandler instead
            Log.i(TAG, "Moving non-focused display " + displayId + " to top "
                    + "because a key is targeting it");
            mWindowManagerFuncs.moveDisplayToTop(displayId);
        }
    }

    return result;
}

public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
        int policyFlags) {
    final boolean keyguardOn = keyguardOn();
    final int keyCode = event.getKeyCode();
    final int repeatCount = event.getRepeatCount();
    final int metaState = event.getMetaState();
    final int flags = event.getFlags();
    final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
    final boolean canceled = event.isCanceled();
    final int displayId = event.getDisplayId();
    final long key_consumed = -1;

    if (DEBUG_INPUT) {
        Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount="
                + repeatCount + " keyguardOn=" + keyguardOn + " canceled=" + canceled);
    }

    if (mKeyCombinationManager.isKeyConsumed(event)) {
        return key_consumed;
    }

    if ((flags & KeyEvent.FLAG_FALLBACK) == 0) {
        final long now = SystemClock.uptimeMillis();
        final long interceptTimeout = mKeyCombinationManager.getKeyInterceptTimeout(keyCode);
        if (now < interceptTimeout) {
            return interceptTimeout - now;
        }
    }

    // Cancel any pending meta actions if we see any other keys being pressed between the down
    // of the meta key and its corresponding up.
    if (mPendingMetaAction && !KeyEvent.isMetaKey(keyCode)) {
        mPendingMetaAction = false;
    }
    // Any key that is not Alt or Meta cancels Caps Lock combo tracking.
    if (mPendingCapsLockToggle && !KeyEvent.isMetaKey(keyCode) && !KeyEvent.isAltKey(keyCode)) {
        mPendingCapsLockToggle = false;
    }

    if (isUserSetupComplete() && !keyguardOn) {
        if (mModifierShortcutManager.interceptKey(event)) {
            dismissKeyboardShortcutsMenu();
            mPendingMetaAction = false;
            mPendingCapsLockToggle = false;
            return key_consumed;
        }
    }

    switch(keyCode) {
        case KeyEvent.KEYCODE_HOME:
            // First we always handle the home key here, so applications
            // can never break it, although if keyguard is on, we do let
            // it handle it, because that gives us the correct 5 second
            // timeout.
            DisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(displayId);
            if (handler == null) {
                handler = new DisplayHomeButtonHandler(displayId);
                mDisplayHomeButtonHandlers.put(displayId, handler);
            }
            return handler.handleHomeButton(focusedToken, event);
        case KeyEvent.KEYCODE_MENU:
            // Hijack modified menu keys for debugging features
            final int chordBug = KeyEvent.META_SHIFT_ON;

            if (down && repeatCount == 0) {
                if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) {
                    Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
                    mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT,
                            null, null, null, 0, null, null);
                    return key_consumed;
                }
            }
            break;
        case KeyEvent.KEYCODE_APP_SWITCH:
            if (!keyguardOn) {
                if (down && repeatCount == 0) {
                    preloadRecentApps();
                } else if (!down) {
                    toggleRecentApps();
                }
            }
            return key_consumed;
        case KeyEvent.KEYCODE_N:
            if (down && event.isMetaPressed()) {
                IStatusBarService service = getStatusBarService();
                if (service != null) {
                    try {
                        service.expandNotificationsPanel();
                    } catch (RemoteException e) {
                        // do nothing.
                    }
                    return key_consumed;
                }
            }
            break;
        case KeyEvent.KEYCODE_S:
            if (down && event.isMetaPressed() && event.isCtrlPressed() && repeatCount == 0) {
                int type = event.isShiftPressed() ? TAKE_SCREENSHOT_SELECTED_REGION
                        : TAKE_SCREENSHOT_FULLSCREEN;
                mScreenshotRunnable.setScreenshotType(type);
                mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER);
                mHandler.post(mScreenshotRunnable);
                return key_consumed;
            }
            break;
        case KeyEvent.KEYCODE_SLASH:
            if (down && repeatCount == 0 && event.isMetaPressed() && !keyguardOn) {
                toggleKeyboardShortcutsMenu(event.getDeviceId());
                return key_consumed;
            }
            break;
        case KeyEvent.KEYCODE_ASSIST:
            Slog.wtf(TAG, "KEYCODE_ASSIST should be handled in interceptKeyBeforeQueueing");
            return key_consumed;
        case KeyEvent.KEYCODE_VOICE_ASSIST:
            Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in"
                    + " interceptKeyBeforeQueueing");
            return key_consumed;
        case KeyEvent.KEYCODE_SYSRQ:
            if (down && repeatCount == 0) {
                mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
                mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER);
                mHandler.post(mScreenshotRunnable);
            }
            return key_consumed;
        case KeyEvent.KEYCODE_BRIGHTNESS_UP:
        case KeyEvent.KEYCODE_BRIGHTNESS_DOWN:
            if (down) {
                int direction = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP ? 1 : -1;

                // Disable autobrightness if it's on
                int auto = Settings.System.getIntForUser(
                        mContext.getContentResolver(),
                        Settings.System.SCREEN_BRIGHTNESS_MODE,
                        Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
                        UserHandle.USER_CURRENT_OR_SELF);
                if (auto != 0) {
                    Settings.System.putIntForUser(mContext.getContentResolver(),
                            Settings.System.SCREEN_BRIGHTNESS_MODE,
                            Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
                            UserHandle.USER_CURRENT_OR_SELF);
                }
                float min = mPowerManager.getBrightnessConstraint(
                        PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
                float max = mPowerManager.getBrightnessConstraint(
                        PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
                float step = (max - min) / BRIGHTNESS_STEPS * direction;
                int screenDisplayId = displayId < 0 ? DEFAULT_DISPLAY : displayId;
                float brightness = mDisplayManager.getBrightness(screenDisplayId);
                brightness += step;
                // Make sure we don't go beyond the limits.
                brightness = Math.min(max, brightness);
                brightness = Math.max(min, brightness);

                mDisplayManager.setBrightness(screenDisplayId, brightness);
                startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG),
                        UserHandle.CURRENT_OR_SELF);
            }
            return key_consumed;
        case KeyEvent.KEYCODE_VOLUME_UP:
        case KeyEvent.KEYCODE_VOLUME_DOWN:
        case KeyEvent.KEYCODE_VOLUME_MUTE:
            if (mUseTvRouting || mHandleVolumeKeysInWM) {
                // On TVs or when the configuration is enabled, volume keys never
                // go to the foreground app.
                dispatchDirectAudioEvent(event);
                return key_consumed;
            }

            // If the device is in VR mode and keys are "internal" (e.g. on the side of the
            // device), then drop the volume keys and don't forward it to the
            // application/dispatch the audio event.
            if (mDefaultDisplayPolicy.isPersistentVrModeEnabled()) {
                final InputDevice d = event.getDevice();
                if (d != null && !d.isExternal()) {
                    return key_consumed;
                }
            }
            break;
        case KeyEvent.KEYCODE_TAB:
            if (event.isMetaPressed()) {
                // Pass through keyboard navigation keys.
                return 0;
            }
            // Display task switcher for ALT-TAB.
            if (down && repeatCount == 0) {
                if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) {
                    final int shiftlessModifiers =
                            event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
                    if (KeyEvent.metaStateHasModifiers(
                            shiftlessModifiers, KeyEvent.META_ALT_ON)) {
                        mRecentAppsHeldModifiers = shiftlessModifiers;
                        showRecentApps(true);
                        return key_consumed;
                    }
                }
            }
            break;
        case KeyEvent.KEYCODE_ALL_APPS:
            if (!down) {
                mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
                Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS);
                msg.setAsynchronous(true);
                msg.sendToTarget();
            }
            return key_consumed;
        case KeyEvent.KEYCODE_NOTIFICATION:
            if (!down) {
                toggleNotificationPanel();
            }
            return key_consumed;

        case KeyEvent.KEYCODE_SPACE:
            // Handle keyboard layout switching.
            if ((metaState & (KeyEvent.META_CTRL_MASK | KeyEvent.META_META_MASK)) == 0) {
                return 0;
            }
            // Share the same behavior with KEYCODE_LANGUAGE_SWITCH.
        case KeyEvent.KEYCODE_LANGUAGE_SWITCH:
            if (down && repeatCount == 0) {
                int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
                mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction);
                return key_consumed;
            }
            break;
        case KeyEvent.KEYCODE_META_LEFT:
        case KeyEvent.KEYCODE_META_RIGHT:
            if (down) {
                if (event.isAltPressed()) {
                    mPendingCapsLockToggle = true;
                    mPendingMetaAction = false;
                } else {
                    mPendingCapsLockToggle = false;
                    mPendingMetaAction = true;
                }
            } else {
                // Toggle Caps Lock on META-ALT.
                if (mPendingCapsLockToggle) {
                    mInputManagerInternal.toggleCapsLock(event.getDeviceId());
                    mPendingCapsLockToggle = false;
                } else if (mPendingMetaAction) {
                    launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
                            event.getDeviceId(),
                            event.getEventTime(), AssistUtils.INVOCATION_TYPE_UNKNOWN);
                    mPendingMetaAction = false;
                }
            }
            return key_consumed;
        case KeyEvent.KEYCODE_ALT_LEFT:
        case KeyEvent.KEYCODE_ALT_RIGHT:
            if (down) {
                if (event.isMetaPressed()) {
                    mPendingCapsLockToggle = true;
                    mPendingMetaAction = false;
                } else {
                    mPendingCapsLockToggle = false;
                }
            } else {
                // hide recent if triggered by ALT-TAB.
                if (mRecentAppsHeldModifiers != 0
                        && (metaState & mRecentAppsHeldModifiers) == 0) {
                    mRecentAppsHeldModifiers = 0;
                    hideRecentApps(true, false);
                    return key_consumed;
                }

                // Toggle Caps Lock on META-ALT.
                if (mPendingCapsLockToggle) {
                    mInputManagerInternal.toggleCapsLock(event.getDeviceId());
                    mPendingCapsLockToggle = false;
                    return key_consumed;
                }
            }
            break;
    }

    if (isValidGlobalKey(keyCode)
            && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {
        return key_consumed;
    }

    // Reserve all the META modifier combos for system behavior
    if ((metaState & KeyEvent.META_META_ON) != 0) {
        return key_consumed;
    }

    // Let the application handle the key.
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498
  • 499
  • 500
  • 501
  • 502
  • 503
  • 504
  • 505
  • 506
  • 507
  • 508
  • 509
  • 510
  • 511
  • 512
  • 513
  • 514
  • 515
  • 516
  • 517
  • 518
  • 519
  • 520
  • 521
  • 522
  • 523
  • 524
  • 525
  • 526
  • 527
  • 528
  • 529
  • 530
  • 531
  • 532
  • 533
  • 534
  • 535
  • 536
  • 537
  • 538
  • 539
  • 540
  • 541
  • 542
  • 543
  • 544
  • 545
  • 546
  • 547
  • 548
  • 549
  • 550
  • 551
  • 552
  • 553
  • 554
  • 555
  • 556
  • 557
  • 558
  • 559
  • 560
  • 561
  • 562
  • 563
  • 564
  • 565
  • 566
  • 567
  • 568
  • 569
  • 570
  • 571
  • 572
  • 573
  • 574
  • 575
  • 576
  • 577
  • 578
  • 579
  • 580
  • 581
  • 582
  • 583
  • 584
  • 585
  • 586
  • 587
  • 588
  • 589
  • 590
  • 591
  • 592
  • 593
  • 594
  • 595
  • 596
  • 597
  • 598
  • 599
  • 600
  • 601
  • 602
  • 603
  • 604
  • 605
  • 606
  • 607
  • 608
  • 609
  • 610
  • 611
  • 612
  • 613
  • 614
  • 615
  • 616
  • 617
  • 618
  • 619
  • 620
  • 621
  • 622
  • 623
  • 624
  • 625
  • 626
  • 627
  • 628
  • 629
  • 630
  • 631
  • 632
  • 633
  • 634
  • 635
  • 636
  • 637
  • 638
  • 639
  • 640
  • 641
  • 642
  • 643
  • 644
  • 645
  • 646
  • 647
  • 648
  • 649
  • 650
  • 651
  • 652
  • 653
  • 654
  • 655
  • 656
  • 657
  • 658
  • 659
  • 660
  • 661
  • 662
  • 663
  • 664
  • 665
  • 666
  • 667
  • 668
  • 669
  • 670
  • 671
  • 672
  • 673
  • 674
  • 675
  • 676
  • 677
  • 678
  • 679
  • 680
  • 681
  • 682
  • 683
  • 684
  • 685
  • 686
  • 687
  • 688
  • 689
  • 690
  • 691
  • 692
  • 693
  • 694
  • 695
  • 696
  • 697
  • 698
  • 699
  • 700
  • 701
  • 702
  • 703
  • 704
  • 705
  • 706
  • 707
  • 708
  • 709
  • 710
  • 711
  • 712
  • 713
  • 714
  • 715
  • 716
  • 717
  • 718
  • 719
  • 720
  • 721
  • 722
  • 723
  • 724
  • 725
  • 726

2.1 没有启动完成KeyEvents不做任何事情,其中KEYCODE_POWER例外

如果我们还没有启动完成,KeyEvents不做任何事情。
例外:唤醒和电源键KeyEvents事件被转发到PowerManager,以允许它在引导期间从静态模式唤醒。

if (!mSystemBooted) {
    // If we have not yet booted, don't let key events do anything.
    // Exception: Wake and power key events are forwarded to PowerManager to allow it to
    // wake from quiescent mode during boot.
    if (down && (keyCode == KeyEvent.KEYCODE_POWER
            || keyCode == KeyEvent.KEYCODE_TV_POWER)) {
        wakeUpFromPowerKey(event.getDownTime());
    } else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP)
            && isWakeKeyWhenScreenOff(keyCode)) {
        wakeUpFromWakeKey(event);
    }
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.2 双击power键启动相机

Android S上 handleKeyGesture() 新增改变,注释解释为了解决多屏显示的场景下出现错误的状态

  • handleCameraGesture() 处理启动相机,最终调用interceptKeyBeforeQueueing() -> handleKeyGesture() -> handleCameraGesture() -> mGestureLauncherService.interceptPowerKeyDown() -> handleCameraGesture() -> StatusBarManagerService.java#onCameraLaunchGestureDetected(source) -> mBar.onCameraLaunchGestureDetected(source) -> StatusBar.java#onCameraLaunchGestureDetected()
    在这里插入图片描述
  • GestureLauncherService.interceptPowerKeyDown()中计算判断
    在这里插入图片描述
  • mBarService.registerStatusBar(mCommandQueue)注册到StatusBarManagerService
    frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
  • 功能开关判断com.android.internal.R.bool.config_cameraDoubleTapPowerGestureEnabled、Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED
// This could prevent some wrong state in multi-displays environment,
// the default display may turned off but interactive is true.
final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
final boolean interactiveAndOn = interactive && isDefaultDisplayOn;
if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
    handleKeyGesture(event, interactiveAndOn);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
private void handleKeyGesture(KeyEvent event, boolean interactive) {
    if (mKeyCombinationManager.interceptKey(event, interactive)) {
        // handled by combo keys manager.
        mSingleKeyGestureDetector.reset();
        return;
    }

    if (event.getKeyCode() == KEYCODE_POWER && event.getAction() == KeyEvent.ACTION_DOWN) {
        mPowerKeyHandled = handleCameraGesture(event, interactive);
        if (mPowerKeyHandled) {
            // handled by camera gesture.
            mSingleKeyGestureDetector.reset();
            return;
        }
    }

    mSingleKeyGestureDetector.interceptKey(event, interactive);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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

public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
        MutableBoolean outLaunched) {
    if (event.isLongPress()) {
        // Long presses are sent as a second key down. If the long press threshold is set lower
        // than the double tap of sequence interval thresholds, this could cause false double
        // taps or consecutive taps, so we want to ignore the long press event.
        return false;
    }
    boolean launchCamera = false;
    boolean launchEmergencyGesture = false;
    boolean intercept = false;
    long powerTapInterval;
    synchronized (this) {
        powerTapInterval = event.getEventTime() - mLastPowerDown;
        mLastPowerDown = event.getEventTime();
        if (powerTapInterval >= POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS) {
            // Tap too slow, reset consecutive tap counts.
            mPowerButtonConsecutiveTaps = 1;
            mPowerButtonSlowConsecutiveTaps = 1;
        } else if (powerTapInterval >= CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS) {
            // Tap too slow for shortcuts
            mPowerButtonConsecutiveTaps = 1;
            mPowerButtonSlowConsecutiveTaps++;
        } else {
            // Fast consecutive tap
            mPowerButtonConsecutiveTaps++;
            mPowerButtonSlowConsecutiveTaps++;
        }
        // Check if we need to launch camera or emergency gesture flows
        if (mEmergencyGestureEnabled) {
            // Commit to intercepting the powerkey event after the second "quick" tap to avoid
            // lockscreen changes between launching camera and the emergency gesture flow.
            if (mPowerButtonConsecutiveTaps > 1) {
                intercept = interactive;
            }
            if (mPowerButtonConsecutiveTaps == EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD) {
                launchEmergencyGesture = true;
            }
        }
        if (mCameraDoubleTapPowerEnabled
                && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
                && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) {
            launchCamera = true;
            intercept = interactive;
        }
    }
    if (mPowerButtonConsecutiveTaps > 1 || mPowerButtonSlowConsecutiveTaps > 1) {
        Slog.i(TAG, Long.valueOf(mPowerButtonConsecutiveTaps)
                + " consecutive power button taps detected, "
                + Long.valueOf(mPowerButtonSlowConsecutiveTaps)
                + " consecutive slow power button taps detected");
    }
    if (launchCamera) {
        Slog.i(TAG, "Power button double tap gesture detected, launching camera. Interval="
                + powerTapInterval + "ms");
        launchCamera = handleCameraGesture(false /* useWakelock */,
                StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
        if (launchCamera) {
            mMetricsLogger.action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE,
                    (int) powerTapInterval);
            mUiEventLogger.log(GestureLauncherEvent.GESTURE_CAMERA_DOUBLE_TAP_POWER);
        }
    } else if (launchEmergencyGesture) {
        Slog.i(TAG, "Emergency gesture detected, launching.");
        launchEmergencyGesture = handleEmergencyGesture();
        mUiEventLogger.log(GestureLauncherEvent.GESTURE_EMERGENCY_TAP_POWER);
    }
    mMetricsLogger.histogram("power_consecutive_short_tap_count",
            mPowerButtonSlowConsecutiveTaps);
    mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval);

    outLaunched.value = launchCamera || launchEmergencyGesture;
    // Intercept power key event if the press is part of a gesture (camera, eGesture) and the
    // user has completed setup.
    return intercept && isUserSetupComplete();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

2.3 五次点击power键启动SOS紧急呼叫

2.2 双击power键启动相机 发现 GestureLauncherService.interceptPowerKeyDown() 中有SOS紧急呼叫功能,也是通过GestureLauncherService\StatusBarManagerService服务调用到StatusBar.java中onEmergencyActionLaunchGestureDetected(),不同的是SOS紧急呼叫功能需要5次(EMERGENCY_GESTURE_POWER_TAP_COUNT_THRESHOLD = 5)点击power键
在这里插入图片描述
在这里插入图片描述

  • 功能开关com.android.internal.R.bool.config_emergencyGestureEnabled、Settings.Secure.EMERGENCY_GESTURE_ENABLED
  • 有个疑问,不知道为什么这样设计,这里是优先处理2.2 双击power键启动相机

2.4 power长按功能 PowerKeyRule,如唤起语音助手、关机、重启等弹框

回到handleKeyGesture(event, interactiveAndOn) power键处理mSingleKeyGestureDetector.interceptKey(event, interactive)

  • PhoneWindowManager.java初始化是在initSingleKeyGestureRules中添加new PowerKeyRule(powerKeyGestures)
  • 长按调用PowerKeyRule中onLongPress(),powerLongPress(eventTime)处理长按功能:
  1. LONG_PRESS_POWER_ASSISTANT 唤醒助手界面launchAssistAction()
  2. LONG_PRESS_POWER_GO_TO_VOICE_ASSIST 唤醒语音助手launchVoiceAssist
  3. LONG_PRESS_POWER_GLOBAL_ACTIONS 弹出关机、重启、飞行模式等选项对话框showGlobalActions()
  4. LONG_PRESS_POWER_SHUT_OFF 关闭所有窗口sendCloseSystemWindows,执行mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF)
  • config_LongPressOnPowerBehavior配置值
    在这里插入图片描述

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

void interceptKey(KeyEvent event, boolean interactive) {
    if (event.getAction() == KeyEvent.ACTION_DOWN) {
        // Store the non interactive state when first down.
        if (mDownKeyCode == KeyEvent.KEYCODE_UNKNOWN || mDownKeyCode != event.getKeyCode()) {
            mBeganFromNonInteractive = !interactive;
        }
        interceptKeyDown(event);
    } else {
        interceptKeyUp(event);
    }
}

private void interceptKeyDown(KeyEvent event) {
     final int keyCode = event.getKeyCode();
     // same key down.
     if (mDownKeyCode == keyCode) {
         if (mActiveRule != null && (event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0
                 && mActiveRule.supportLongPress() && !mHandledByLongPress) {
             if (DEBUG) {
                 Log.i(TAG, "Long press key " + KeyEvent.keyCodeToString(keyCode));
             }
             mHandledByLongPress = true;
             mHandler.removeMessages(MSG_KEY_LONG_PRESS);
             mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
             mActiveRule.onLongPress(event.getEventTime());
         }
         return;
     }
     // ... ...
     mDownKeyCode = keyCode;
     // ... ...
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

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

private void initSingleKeyGestureRules() {
    mSingleKeyGestureDetector = new SingleKeyGestureDetector(mContext);

    int powerKeyGestures = 0;
    if (hasVeryLongPressOnPowerBehavior()) {
        powerKeyGestures |= KEY_VERYLONGPRESS;
    }
    if (hasLongPressOnPowerBehavior()) {
        powerKeyGestures |= KEY_LONGPRESS;
    }
    mSingleKeyGestureDetector.addRule(new PowerKeyRule(powerKeyGestures));

    if (hasLongPressOnBackBehavior()) {
        mSingleKeyGestureDetector.addRule(new BackKeyRule(KEY_LONGPRESS));
    }
}

private final class PowerKeyRule extends SingleKeyGestureDetector.SingleKeyRule {

    @Override
    void onLongPress(long eventTime) {
        if (mSingleKeyGestureDetector.beganFromNonInteractive()
                && !mSupportLongPressPowerWhenNonInteractive) {
            Slog.v(TAG, "Not support long press power when device is not interactive.");
            return;
        }

        powerLongPress(eventTime);
    }
}

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, 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, 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, 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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

2.5 power键亮灭屏

接着查看power键点击亮灭屏,KeyEvent.ACTION_DOWN按下interceptKeyDown\ KeyEvent.ACTION_UP抬起interceptKeyUp;关注抬起后操作未触发长按继续处理,可能是多次点击power键延时处理MSG_KEY_DELAYED_PRESS,看到onPress()\onMultiPress()最终都是调用powerPress(),这里没有处理通知亮屏逻辑。

  • 延时时间MULTI_PRESS_TIMEOUT = ViewConfiguration.getMultiPressTimeout(),默认时间是DEFAULT_MULTI_PRESS_TIMEOUT = 300
    在这里插入图片描述
  • 查看时,mShortPressOnPowerBehavior基本有两种行为动作
  1. SHORT_PRESS_POWER_GO_TO_SLEEP 灭屏sleepDefaultDisplayFromPowerButton()
  2. SHORT_PRESS_POWER_GO_HOME回到HOME桌面
  • config_shortPressOnPowerBehavior配置值
    在这里插入图片描述

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

void interceptKey(KeyEvent event, boolean interactive) {
    if (event.getAction() == KeyEvent.ACTION_DOWN) {
        // Store the non interactive state when first down.
        if (mDownKeyCode == KeyEvent.KEYCODE_UNKNOWN || mDownKeyCode != event.getKeyCode()) {
            mBeganFromNonInteractive = !interactive;
        }
        interceptKeyDown(event);
    } else {
        interceptKeyUp(event);
    }
}

private void interceptKeyDown(KeyEvent event) {
    // ... ...
}

private boolean interceptKeyUp(KeyEvent event) {
    mHandler.removeMessages(MSG_KEY_LONG_PRESS);
    mHandler.removeMessages(MSG_KEY_VERY_LONG_PRESS);
    mDownKeyCode = KeyEvent.KEYCODE_UNKNOWN;
    if (mActiveRule == null) {
        return false;
    }

    if (mHandledByLongPress) {
        mHandledByLongPress = false;
        mKeyPressCounter = 0;
        return true;
    }

    final long downTime = event.getDownTime();
    if (event.getKeyCode() == mActiveRule.mKeyCode) {
        // Directly trigger short press when max count is 1.
        if (mActiveRule.getMaxMultiPressCount() == 1) {
            if (DEBUG) {
                Log.i(TAG, "press key " + KeyEvent.keyCodeToString(event.getKeyCode()));
            }
            Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode,
                    1, downTime);
            msg.setAsynchronous(true);
            mHandler.sendMessage(msg);
            return true;
        }

        // This could be a multi-press.  Wait a little bit longer to confirm.
        mKeyPressCounter++;
        Message msg = mHandler.obtainMessage(MSG_KEY_DELAYED_PRESS, mActiveRule.mKeyCode,
                mKeyPressCounter, downTime);
        msg.setAsynchronous(true);
        mHandler.sendMessageDelayed(msg, MULTI_PRESS_TIMEOUT);
        return true;
    }
    reset();
    return false;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

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

private final class PowerKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
    // ... ...
    @Override
    void onPress(long downTime) {
        powerPress(downTime, 1 /*count*/,
                mSingleKeyGestureDetector.beganFromNonInteractive());
    }
    // ... ...
    @Override
    void onMultiPress(long downTime, int count) {
        powerPress(downTime, count, mSingleKeyGestureDetector.beganFromNonInteractive());
    }
}

private void powerPress(long eventTime, int count, boolean beganFromNonInteractive) {
    // ... ...
    if (count == 2) {
        powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
    } else if (count == 3) {
        powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
    } else if (interactive && !beganFromNonInteractive) {
        if (mSideFpsEventHandler.onSinglePressDetected(eventTime)) {
            Slog.i(TAG, "Suppressing power key because the user is interacting with the "
                    + "fingerprint sensor");
            return;
        }
        switch (mShortPressOnPowerBehavior) {
            case SHORT_PRESS_POWER_NOTHING:
                break;
            case SHORT_PRESS_POWER_GO_TO_SLEEP:
                sleepDefaultDisplayFromPowerButton(eventTime, 0);
                break;
            case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
                sleepDefaultDisplayFromPowerButton(eventTime,
                        PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                break;
            case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
                if (sleepDefaultDisplayFromPowerButton(eventTime,
                        PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE)) {
                    launchHomeFromHotKey(DEFAULT_DISPLAY);
                }
                break;
            case SHORT_PRESS_POWER_GO_HOME:
                shortPressPowerGoHome();
                break;
            case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: {
                if (mDismissImeOnBackKeyPressed) {
                    if (mInputMethodManagerInternal == null) {
                        mInputMethodManagerInternal =
                                LocalServices.getService(InputMethodManagerInternal.class);
                    }
                    if (mInputMethodManagerInternal != null) {
                        mInputMethodManagerInternal.hideCurrentInputMethod(
                                SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME);
                    }
                } else {
                    shortPressPowerGoHome();
                }
                break;
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
2.5.1 power键亮屏

Android S上 handleKeyGesture() 新增改变处理长按、连击、灭屏等事件,优先处理组合按键功能KeyCombinationManager,继续查看interceptKeyBeforeQueueing()代码,发现PhoneWindowManager.java中有power键处理interceptPowerKeyDown()、interceptPowerKeyUp(),亮屏处理就是在interceptPowerKeyDown()中:

  • mPowerKeyWakeLock.acquire()持有power的WakeLock;此处可能会耗时,有可能WakeLock底层未释放
  • wakeUpFromPowerKey(event.getDownTime()) wakeUp亮屏
// Handle special keys.
switch (keyCode) {
    case KeyEvent.KEYCODE_POWER: {
        // ... ...
        EventLogTags.writeInterceptPower(
                KeyEvent.actionToString(event.getAction()),
                mPowerKeyHandled ? 1 : 0,
                mSingleKeyGestureDetector.getKeyPressCounter(KeyEvent.KEYCODE_POWER));
        // Any activity on the power button stops the accessibility shortcut
        result &= ~ACTION_PASS_TO_USER;
        isWakeKey = false; // wake-up will be handled separately
        if (down) {
            interceptPowerKeyDown(event, interactiveAndOn);
        } else {
            interceptPowerKeyUp(event, canceled);
        }
        break;
    }
    // ... ...
}

private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
    // Hold a wake lock until the power key is released.
    if (!mPowerKeyWakeLock.isHeld()) {
        mPowerKeyWakeLock.acquire();
    }

    mWindowManagerFuncs.onPowerKeyDown(interactive);

    // Stop ringing or end call if configured to do so when power is pressed.
    TelecomManager telecomManager = getTelecommService();
    boolean hungUp = false;
    if (telecomManager != null) {
        if (telecomManager.isRinging()) {
            // Pressing Power while there's a ringing incoming
            // call should silence the ringer.
            telecomManager.silenceRinger();
        } else if ((mIncallPowerBehavior
                & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
                && telecomManager.isInCall() && interactive) {
            // Otherwise, if "Power button ends call" is enabled,
            // the Power button will hang up any current active call.
            hungUp = telecomManager.endCall();
        }
    }

    final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);

    // Inform the StatusBar; but do not allow it to consume the event.
    sendSystemKeyToStatusBarAsync(event.getKeyCode());

    // 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 = mPowerKeyHandled || hungUp
            || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
    if (!mPowerKeyHandled) {
        if (!interactive) {
            wakeUpFromPowerKey(event.getDownTime());
        }
    } else {
        // handled by another power key policy.
        if (!mSingleKeyGestureDetector.isKeyIntercepted(KEYCODE_POWER)) {
            mSingleKeyGestureDetector.reset();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

2.6 音量上下键调节音量KEYCODE_VOLUME_DOWN\KEYCODE_VOLUME_UP

KeyEvent.KEYCODE_VOLUME_MUTE 这个一般就是耳机上的按键,主要控制音乐暂停\播放等。这里主要查看KEYCODE_VOLUME_DOWN\KEYCODE_VOLUME_UP按键:

  • sendSystemKeyToStatusBarAsync(event.getKeyCode())通知到SystemUI中StatusBar.java#handleSystemKey(),这里看到只有按下DOWN时通知,没有看到抬起UP时通知
  • MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent() 调用MediaSessionService.java#dispatchVolumeKeyEvent()调节音量
// Handle special keys.
switch (keyCode) {
    // ... ...
	case KeyEvent.KEYCODE_VOLUME_DOWN:
	case KeyEvent.KEYCODE_VOLUME_UP:
	case KeyEvent.KEYCODE_VOLUME_MUTE: {
	    if (down) {
	        sendSystemKeyToStatusBarAsync(event.getKeyCode());
	
	        NotificationManager nm = getNotificationService();
	        if (nm != null && !mHandleVolumeKeysInWM) {
	            nm.silenceNotificationSound();
	        }
	
	        TelecomManager telecomManager = getTelecommService();
	        if (telecomManager != null && !mHandleVolumeKeysInWM) {
	            // When {@link #mHandleVolumeKeysInWM} is set, volume key events
	            // should be dispatched to WM.
	            if (telecomManager.isRinging()) {
	                // If an incoming call is ringing, either VOLUME key means
	                // "silence ringer".  We handle these keys here, rather than
	                // in the InCallScreen, to make sure we'll respond to them
	                // even if the InCallScreen hasn't come to the foreground yet.
	                // Look for the DOWN event here, to agree with the "fallback"
	                // behavior in the InCallScreen.
	                Log.i(TAG, "interceptKeyBeforeQueueing:"
	                      + " VOLUME key-down while ringing: Silence ringer!");
	
	                // Silence the ringer.  (It's safe to call this
	                // even if the ringer has already been silenced.)
	                telecomManager.silenceRinger();
	
	                // And *don't* pass this key thru to the current activity
	                // (which is probably the InCallScreen.)
	                result &= ~ACTION_PASS_TO_USER;
	                break;
	            }
	        }
	        int audioMode = AudioManager.MODE_NORMAL;
	        try {
	            audioMode = getAudioService().getMode();
	        } catch (Exception e) {
	            Log.e(TAG, "Error getting AudioService in interceptKeyBeforeQueueing.", e);
	        }
	        boolean isInCall = (telecomManager != null && telecomManager.isInCall()) ||
	                audioMode == AudioManager.MODE_IN_COMMUNICATION;
	        if (isInCall && (result & ACTION_PASS_TO_USER) == 0) {
	            // If we are in call but we decided not to pass the key to
	            // the application, just pass it to the session service.
	            MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
	                    event, AudioManager.USE_DEFAULT_STREAM_TYPE, false);
	            break;
	        }
	    }
	    if (mUseTvRouting || mHandleVolumeKeysInWM) {
	        // Defer special key handlings to
	        // {@link interceptKeyBeforeDispatching()}.
	        result |= ACTION_PASS_TO_USER;
	    } else if ((result & ACTION_PASS_TO_USER) == 0) {
	        // If we aren't passing to the user and no one else
	        // handled it send it to the session manager to
	        // figure out.
	        MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent(
	                event, AudioManager.USE_DEFAULT_STREAM_TYPE, true);
	    }
	    break;
	}
	// ... ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

3. 实体按键组合功能KeyCombinationManager

在此查看handleKeyGesture方法,优先处理KeyCombinationManager.interceptKey();下面具体查看initKeyCombinationRules()组合按键规则添加

private void handleKeyGesture(KeyEvent event, boolean interactive) {
    if (mKeyCombinationManager.interceptKey(event, interactive)) {
        // handled by combo keys manager.
        mSingleKeyGestureDetector.reset();
        return;
    }

    if (event.getKeyCode() == KEYCODE_POWER && event.getAction() == KeyEvent.ACTION_DOWN) {
        mPowerKeyHandled = handleCameraGesture(event, interactive);
        if (mPowerKeyHandled) {
            // handled by camera gesture.
            mSingleKeyGestureDetector.reset();
            return;
        }
    }

    mSingleKeyGestureDetector.interceptKey(event, interactive);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

3.1 POWER+VOLUME_DOWN截图功能

initKeyCombinationRules()添加new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER),截图interceptScreenshotChord()

final boolean screenshotChordEnabled = mContext.getResources().getBoolean(
        com.android.internal.R.bool.config_enableScreenshotChord);

if (screenshotChordEnabled) {
    mKeyCombinationManager.addRule(
            new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) {
                @Override
                void execute() {
                    mPowerKeyHandled = true;
                    interceptScreenshotChord();
                }
                @Override
                void cancel() {
                    cancelPendingScreenshotChordAction();
                }
            });
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

3.2 VOLUME_DOWN+VOLUME_UP辅助功能快捷方式

initKeyCombinationRules()添加new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_VOLUME_UP),执行interceptAccessibilityShortcutChord()激活辅助功能accessibilityShortcutActivated()

mKeyCombinationManager.addRule(
        new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_VOLUME_UP) {
            @Override
            boolean preCondition() {
                return mAccessibilityShortcutController
                        .isAccessibilityShortcutAvailable(isKeyguardLocked());
            }
            @Override
            void execute() {
                interceptAccessibilityShortcutChord();
            }
            @Override
            void cancel() {
                cancelPendingAccessibilityShortcutAction();
            }
        });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3.3 POWER+VOLUME_UP 静音

initKeyCombinationRules()添加new TwoKeysCombinationRule(KEYCODE_VOLUME_UP, KEYCODE_POWER),执行execute()执行中区分两种行为:

  • POWER_VOLUME_UP_BEHAVIOR_MUTE 执行 interceptRingerToggleChord() 设置 mAudioManagerInternal.silenceRingerModeInternal("volume_hush") 静音模式RINGER_MODE_SILENT
  • showGlobalActions() 执行 showGlobalActions() 弹出关机、重启、飞行模式等选项对话框;power长按功能中也有行为实现该功能
// Volume up + power can either be the "ringer toggle chord" or as another way to
// launch GlobalActions. This behavior can change at runtime so we must check behavior
// inside the TwoKeysCombinationRule.
mKeyCombinationManager.addRule(
        new TwoKeysCombinationRule(KEYCODE_VOLUME_UP, KEYCODE_POWER) {
            @Override
            boolean preCondition() {
                switch (mPowerVolUpBehavior) {
                    case POWER_VOLUME_UP_BEHAVIOR_MUTE:
                        return mRingerToggleChord != VOLUME_HUSH_OFF;
                    default:
                        return true;
                }
            }
            @Override
            void execute() {
                switch (mPowerVolUpBehavior) {
                    case POWER_VOLUME_UP_BEHAVIOR_MUTE:
                        // no haptic feedback here since
                        interceptRingerToggleChord();
                        mPowerKeyHandled = true;
                        break;
                    case POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS:
                        performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false,
                                "Power + Volume Up - Global Actions");
                        showGlobalActions();
                        mPowerKeyHandled = true;
                        break;
                    default:
                        break;
                }
            }
            @Override
            void cancel() {
                switch (mPowerVolUpBehavior) {
                    case POWER_VOLUME_UP_BEHAVIOR_MUTE:
                        cancelPendingRingerToggleChordAction();
                        break;
                    case POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS:
                        cancelGlobalActionsAction();
                        break;
                }
            }
        });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

4. Kernel按键驱动

Linux Kernel下按键驱动 drivers/input/keyboard/gpio_keys.c:实现了一个体系结构无关的GPIO按键驱动,使用此按键驱动,只需在设备树gpio-key节点添加需要的按键子节点即可。
(相关文章 Linux 驱动之gpio-key驱动分析gpio_key按键驱动Android添加新按键)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/713781
推荐阅读
相关标签
  

闽ICP备14008679号