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

Posted xhBruce

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了InputManagerService实体按键及组合按键-Android12相关的知识,希望对你有一定的参考价值。

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 从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;
            InputManagerService实体按键及组合按键-Android12

Android R input 之 InputManagerService 的建立

Android R input 之 InputManagerService 的建立

Power按键流程分析

IMS:InputManagerService小结

Android输入系统——InputManagerService