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 的建立