主要涉及的文件有:
WindowManagerService.java frameworks\base\services\java\com\android\server\
PhoneWindow.java frameworks\policies\base\phone\com\android\internal\policy\impl
KeyInputQueue.java frameworks\base\services\java\com\android\server
com_android_server_KeyInputQueue.cpp frameworks\base\services\jni
EventHub.cpp frameworks\base\libs\ui
WindowManagerService.java主要有两个线程,一个负责分发按键的InputDisapath Thread,另一个负责从底层读取按键消息InputDeviceRender Thread。
WindowManagerService.java的成员类KeyQ(),负责获取各种按键设备的状态,它继承于KeyInputQueue类。通过线程InputDeviceRender Thread的readEvent对按键消息不停读取,然后调用KeyQ实例化后的processEvent函数告诉该按键是否应该传给上层。接着WindowManagerService通过InputDisPatch Thread在按键消息队列里取出,并进行分发。
由此可知,InputDisapath线程负责分发,InputDeviceRender线程通过jni方式调用android_server_KeyInputQueue_readEvent(),在这里负责转化C++的按键消息为java的格式,android_server_KeyInputQueue_readEvent在EventHub.cpp中获取按键消息。
具体一些细节代码如下:
WindowManagerService中的KeyQ()类,preporcessEvent函数负责对按键进行预处理, 主要的事件类型包括EV_KEY(按键事件)、EV_REL(相对值,如鼠标移动,报告相对于最后一次位置的偏移)和EV_ABS(绝对值,如触摸屏)。
[java] view plain copy print ?
- @Override
- boolean preprocessEvent(InputDevice device, RawInputEvent event) {
- if (mPolicy.preprocessInputEventTq(event)) {
- return true;
- }
-
- switch (event.type) {
- case RawInputEvent.EV_KEY: {
-
- if (DEBUG) {
- if (event.keycode == KeyEvent.KEYCODE_G) {
- if (event.value != 0) {
-
- mPolicy.screenTurnedOff(WindowManagerPolicy.OFF_BECAUSE_OF_USER);
- }
- return false;
- }
- if (event.keycode == KeyEvent.KEYCODE_D) {
- if (event.value != 0) {
-
- }
- return false;
- }
- }
-
-
- boolean screenIsOff = !mPowerManager.isScreenOn();
- boolean screenIsDim = !mPowerManager.isScreenBright();
- int actions = mPolicy.interceptKeyTq(event, !screenIsOff);/
-
- if ((actions & WindowManagerPolicy.ACTION_GO_TO_SLEEP) != 0) {
- mPowerManager.goToSleep(event.when);
- }
-
- if (screenIsOff) {
- event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
- }
- if (screenIsDim) {
- event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
- }
- if ((actions & WindowManagerPolicy.ACTION_POKE_USER_ACTIVITY) != 0) {
- mPowerManager.userActivity(event.when, false,
- LocalPowerManager.BUTTON_EVENT, false);
- }
-
- if ((actions & WindowManagerPolicy.ACTION_PASS_TO_USER) != 0) {
- if (event.value != 0 && mPolicy.isAppSwitchKeyTqTiLwLi(event.keycode)) {
- filterQueue(this);
- mKeyWaiter.appSwitchComing();
- }
- return true;
- } else {
- return false;
- }
- }
-
- case RawInputEvent.EV_REL: {
- boolean screenIsOff = !mPowerManager.isScreenOn();
- boolean screenIsDim = !mPowerManager.isScreenBright();
- if (screenIsOff) {
- if (!mPolicy.isWakeRelMovementTq(event.deviceId,
- device.classes, event)) {
-
- return false;
- }
- event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
- }
- if (screenIsDim) {
- event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
- }
- return true;
- }
-
- case RawInputEvent.EV_ABS: {
- boolean screenIsOff = !mPowerManager.isScreenOn();
- boolean screenIsDim = !mPowerManager.isScreenBright();
- if (screenIsOff) {
- if (!mPolicy.isWakeAbsMovementTq(event.deviceId,
- device.classes, event)) {
-
- return false;
- }
- event.flags |= WindowManagerPolicy.FLAG_WOKE_HERE;
- }
- if (screenIsDim) {
- event.flags |= WindowManagerPolicy.FLAG_BRIGHT_HERE;
- }
- return true;
- }
-
- default:
- return true;
- }
- }
preporcessEvent调用了InterceptKeyTQ
PhoneWindowManager.java中的InterceptKeyTQ判断该按键是否应该送给上层,还是在此层进行截取,如待机休眠唤醒则在此层进行截取。
[java] view plain copy print ?
-
[java] view plain copy print ?
-
- public int interceptKeyTq(RawInputEvent event, boolean screenIsOn) {
- int result = ACTION_PASS_TO_USER;
- final boolean isWakeKey = isWakeKeyTq(event);
-
-
-
-
- final boolean keyguardActive = (screenIsOn ?
- mKeyguardMediator.isShowingAndNotHidden() :
- mKeyguardMediator.isShowing());
-
-
- if (false) {
- Log.d(TAG, "interceptKeyTq event=" + event + " keycode=" + event.keycode
- + " screenIsOn=" + screenIsOn + " keyguardActive=" + keyguardActive);
- }
-
-
- if (keyguardActive) {
- if (screenIsOn) {
-
- result |= ACTION_PASS_TO_USER;
- } else {
-
- result &= ~ACTION_PASS_TO_USER;
-
-
- final boolean isKeyDown =
- (event.type == RawInputEvent.EV_KEY) && (event.value != 0);
- if (isWakeKey && isKeyDown) {
-
-
-
-
-
- if (!mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(event.keycode)
- && (event.keycode == KeyEvent.KEYCODE_VOLUME_DOWN
- || event.keycode == KeyEvent.KEYCODE_VOLUME_UP)) {
-
-
- if (isInCall()) {
- handleVolumeKey(AudioManager.STREAM_VOICE_CALL, event.keycode);
- } else if (isMusicActive()) {
- handleVolumeKey(AudioManager.STREAM_MUSIC, event.keycode);
- }
- }
- }
- }
- } else if (!screenIsOn) {
-
-
-
-
- if (isInCall() && event.type == RawInputEvent.EV_KEY &&
- (event.keycode == KeyEvent.KEYCODE_VOLUME_DOWN
- || event.keycode == KeyEvent.KEYCODE_VOLUME_UP)) {
- result &= ~ACTION_PASS_TO_USER;
- handleVolumeKey(AudioManager.STREAM_VOICE_CALL, event.keycode);
- }
- if (isWakeKey) {
-
-
- result |= ACTION_POKE_USER_ACTIVITY;
- result &= ~ACTION_PASS_TO_USER;
- }
- }
-
-
- int type = event.type;
- int code = event.keycode;
- boolean down = event.value != 0;
-
-
- if (type == RawInputEvent.EV_KEY) {
- if (code == KeyEvent.KEYCODE_ENDCALL
- || code == KeyEvent.KEYCODE_POWER) {
- if (down) {
- boolean handled = false;
- boolean hungUp = false;
-
-
-
- ITelephony phoneServ = getPhoneInterface();
- if (phoneServ != null) {
- try {
- if (code == KeyEvent.KEYCODE_ENDCALL) {
- handled = hungUp = phoneServ.endCall();
- } else if (code == KeyEvent.KEYCODE_POWER) {
- if (phoneServ.isRinging()) {
-
-
- phoneServ.silenceRinger();
- handled = true;
- } else if (phoneServ.isOffhook() &&
- ((mIncallPowerBehavior
- & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP)
- != 0)) {
-
-
- handled = hungUp = phoneServ.endCall();
- }
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "ITelephony threw RemoteException" + ex);
- }
- } else {
- Log.w(TAG, "!!! Unable to find ITelephony interface !!!");
- }
-
-
- if (!screenIsOn
- || (handled && code != KeyEvent.KEYCODE_POWER)
- || (handled && hungUp && code == KeyEvent.KEYCODE_POWER)) {
- mShouldTurnOffOnKeyUp = false;
- } else {
-
- mShouldTurnOffOnKeyUp = true;
- mHandler.postDelayed(mPowerLongPress,
- ViewConfiguration.getGlobalActionKeyTimeout());
- result &= ~ACTION_PASS_TO_USER;
- }
- } else {
- mHandler.removeCallbacks(mPowerLongPress);
- if (mShouldTurnOffOnKeyUp) {
- mShouldTurnOffOnKeyUp = false;
- boolean gohome, sleeps;
- if (code == KeyEvent.KEYCODE_ENDCALL) {
- gohome = (mEndcallBehavior
- & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0;
- sleeps = (mEndcallBehavior
- & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0;
- } else {
- gohome = false;
- sleeps = true;
- }
- if (keyguardActive
- || (sleeps && !gohome)
- || (gohome && !goHome() && sleeps)) {
-
-
- Log.d(TAG, "I'm tired mEndcallBehavior=0x"
- + Integer.toHexString(mEndcallBehavior));
- result &= ~ACTION_POKE_USER_ACTIVITY;
- result |= ACTION_GO_TO_SLEEP;
- }
- result &= ~ACTION_PASS_TO_USER;
- }
- }
- } else if (isMediaKey(code)) {
-
-
-
- if ((result & ACTION_PASS_TO_USER) == 0) {
-
-
-
- KeyEvent keyEvent = new KeyEvent(event.when, event.when,
- down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
- code, 0);
- mBroadcastWakeLock.acquire();
- mHandler.post(new PassHeadsetKey(keyEvent));
- }
- } else if (code == KeyEvent.KEYCODE_CALL) {
-
-
-
-
-
-
-
-
- if (down) {
- try {
- ITelephony phoneServ = getPhoneInterface();
- if (phoneServ != null) {
- if (phoneServ.isRinging()) {
- Log.i(TAG, "interceptKeyTq:"
- + " CALL key-down while ringing: Answer the call!");
- phoneServ.answerRingingCall();
-
-
-
-
- result &= ~ACTION_PASS_TO_USER;
- }
- } else {
- Log.w(TAG, "CALL button: Unable to find ITelephony interface");
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "CALL button: RemoteException from getPhoneInterface()", ex);
- }
- }
- } else if ((code == KeyEvent.KEYCODE_VOLUME_UP)
- || (code == KeyEvent.KEYCODE_VOLUME_DOWN)) {
-
-
-
-
-
-
-
-
- if (down) {
- try {
- ITelephony phoneServ = getPhoneInterface();
- if (phoneServ != null) {
- if (phoneServ.isRinging()) {
- Log.i(TAG, "interceptKeyTq:"
- + " VOLUME key-down while ringing: Silence ringer!");
-
-
- phoneServ.silenceRinger();
-
-
-
-
- result &= ~ACTION_PASS_TO_USER;
- }
- } else {
- Log.w(TAG, "VOLUME button: Unable to find ITelephony interface");
- }
- } catch (RemoteException ex) {
- Log.w(TAG, "VOLUME button: RemoteException from getPhoneInterface()", ex);
- }
- }
- }
- }
-
-
- return result;
- }
WindowManagerService中的InputDispatcherThread线程process,在里头调用mQueue(KeyQ类)的getEvent函数来获取队列中的消息,处理后分发。
[java] view plain copy print ?
- private void process() {
- android.os.Process.setThreadPriority(
- android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
-
-
- KeyEvent lastKey = null;
-
-
- long lastKeyTime = SystemClock.uptimeMillis();
- long nextKeyTime = lastKeyTime+LONG_WAIT;
- long downTime = 0;
-
-
- int keyRepeatCount = 0;
-
-
- boolean configChanged = false;
-
- while (true) {
- long curTime = SystemClock.uptimeMillis();
-
- if (DEBUG_INPUT) Slog.v(
- TAG, "Waiting for next key: now=" + curTime
- + ", repeat @ " + nextKeyTime);
-
-
-
-
-
- QueuedEvent ev = mQueue.getEvent(
- (int)((!configChanged && curTime < nextKeyTime)
- ? (nextKeyTime-curTime) : 0));
-
- if (DEBUG_INPUT && ev != null) Slog.v(
- TAG, "Event: type=" + ev.classType + " data=" + ev.event);
-
- if (MEASURE_LATENCY) {
- lt.sample("2 got event ", System.nanoTime() - ev.whenNano);
- }
-
- if (lastKey != null && !mPolicy.allowKeyRepeat()) {
-
- lastKey = null;
- downTime = 0;
- lastKeyTime = curTime;
- nextKeyTime = curTime + LONG_WAIT;
- }
- try {
- if (ev != null) {
- curTime = SystemClock.uptimeMillis();
- int eventType;
- if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN) {
- eventType = eventType((MotionEvent)ev.event);
- } else if (ev.classType == RawInputEvent.CLASS_KEYBOARD ||
- ev.classType == RawInputEvent.CLASS_TRACKBALL) {
- eventType = LocalPowerManager.BUTTON_EVENT;
- } else {
- eventType = LocalPowerManager.OTHER_EVENT;
- }
- try {
- if ((curTime - mLastBatteryStatsCallTime)
- >= MIN_TIME_BETWEEN_USERACTIVITIES) {
- mLastBatteryStatsCallTime = curTime;
- mBatteryStats.noteInputEvent();
- }
- } catch (RemoteException e) {
-
- }
-
- if (ev.classType == RawInputEvent.CLASS_CONFIGURATION_CHANGED) {
-
- } else if (eventType != TOUCH_EVENT
- && eventType != LONG_TOUCH_EVENT
- && eventType != CHEEK_EVENT) {
- mPowerManager.userActivity(curTime, false,
- eventType, false);
- } else if (mLastTouchEventType != eventType
- || (curTime - mLastUserActivityCallTime)
- >= MIN_TIME_BETWEEN_USERACTIVITIES) {
- mLastUserActivityCallTime = curTime;
- mLastTouchEventType = eventType;
- mPowerManager.userActivity(curTime, false,
- eventType, false);
- }
-
- switch (ev.classType) {
- case RawInputEvent.CLASS_KEYBOARD:
- KeyEvent ke = (KeyEvent)ev.event;
- if (ke.isDown()) {
- lastKey = ke;
- downTime = curTime;
- keyRepeatCount = 0;
- lastKeyTime = curTime;
- nextKeyTime = lastKeyTime
- + ViewConfiguration.getLongPressTimeout();
- if (DEBUG_INPUT) Slog.v(
- TAG, "Received key down: first repeat @ "
- + nextKeyTime);
- } else {
- lastKey = null;
- downTime = 0;
-
- lastKeyTime = curTime;
- nextKeyTime = curTime + LONG_WAIT;
- if (DEBUG_INPUT) Slog.v(
- TAG, "Received key up: ignore repeat @ "
- + nextKeyTime);
- }
- dispatchKey((KeyEvent)ev.event, 0, 0);
- mQueue.recycleEvent(ev);
- break;
- case RawInputEvent.CLASS_TOUCHSCREEN:
-
- dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
- break;
- case RawInputEvent.CLASS_TRACKBALL:
- dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0);
- break;
- case RawInputEvent.CLASS_CONFIGURATION_CHANGED:
- configChanged = true;
- break;
- default:
- mQueue.recycleEvent(ev);
- break;
- }
-
- } else if (configChanged) {
- configChanged = false;
- sendNewConfiguration();
-
- } else if (lastKey != null) {
- curTime = SystemClock.uptimeMillis();
-
-
-
- if (DEBUG_INPUT) Slog.v(
- TAG, "Key timeout: repeat=" + nextKeyTime
- + ", now=" + curTime);
- if (curTime < nextKeyTime) {
- continue;
- }
-
- lastKeyTime = nextKeyTime;
- nextKeyTime = nextKeyTime + KEY_REPEAT_DELAY;
- keyRepeatCount++;
- if (DEBUG_INPUT) Slog.v(
- TAG, "Key repeat: count=" + keyRepeatCount
- + ", next @ " + nextKeyTime);
- KeyEvent newEvent;
- if (downTime != 0 && (downTime
- + ViewConfiguration.getLongPressTimeout())
- <= curTime) {
- newEvent = KeyEvent.changeTimeRepeat(lastKey,
- curTime, keyRepeatCount,
- lastKey.getFlags() | KeyEvent.FLAG_LONG_PRESS);
- downTime = 0;
- } else {
- newEvent = KeyEvent.changeTimeRepeat(lastKey,
- curTime, keyRepeatCount);
- }
- dispatchKey(newEvent, 0, 0);
-
- } else {
- curTime = SystemClock.uptimeMillis();
-
- lastKeyTime = curTime;
- nextKeyTime = curTime + LONG_WAIT;
- }
-
- } catch (Exception e) {
- Slog.e(TAG,
- "Input thread received uncaught exception: " + e, e);
- }
- }
- }
- }
个人水平有限,有错误欢迎指出,谢谢。
补充:
1、生成
存在这样一个线程,它不断地从driver读取Event,并把它放到RawEvent队列中。这个队列中的RawEvent既有按键,也有触摸、轨迹球等事件。
RawEvent队列中的每个RawEvent最后都会通过一系列转化,最终变为KeyEvent被发送给另外一个线程,即输入线程,也就是一个Activity的主线程。
2、传递
KeyEvent传递过程主要可以划分为三步:过滤器、View树、Activity
过滤器部分主要对应着PhoneWindowManager.java中的interceptKeyTq和interceptKeyTi这两个方法。它们的代码可以在frameworks/base/policy/base/phone/com/Android/internal/policy/impl/PhoneWindowManager.java中看到。
这两个过滤器最大的不同就是interceptKeyTq用于RawEvent,而interceptKeyTi用于KeyEvent。
在一个没有实体键盘的机器上,Power键会被interceptKeyTq这个过滤器吃掉用来调用关机对话框或者使机器休眠。而Home键会被interceptKeyTi这个过滤器吃掉,用来把当前Activity切换到后台并把桌面程序切换到前台。所以,应用程序在View和Activity的onKeyDown/Up中是监听不到这两个按键的。除了这两个键以外的按键,都有机会继续前进。接下来,KeyEvent会先经过interceptKeyTi过滤器,如果这个过滤器不吃掉的话,就会继续前进,进入View树,如果没有被哪个View吃掉的话,最后进入到Activity的onKeyDown/Up方法中。
当一个KeyEvent经过上面的过程还没有被吃掉的话,系统就会利用它做一些定制的功能。比如音量键被系统用来调整声音,多媒体按键用来控制媒体播放,搜索键用来快速打开搜索功能,返回键用来退出当前Activity等。