Android通过 KeyInputQ在WindowMangerService中建立一个独立的线程InputDeviceReader,使用Native函数readEvent来读取Linux Driver的数据构建RawEvent,并放入到KeyQ消息队列中。
KeyInputQueue.java
Thread mThread = new Thread("InputDeviceReader") {
public void run() {
if (DEBUG) Log.v(TAG, "InputDeviceReader.run()");
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
RawInputEvent ev = new RawInputEvent();
while (true) {
try {
InputDevice di;
// block, doesn't release the monitor
readEvent(ev);
boolean send = false;
boolean configChanged = false;
if (false) {
Log.i(TAG, "Input event: dev=0x"
+ Integer.toHexString(ev.deviceId)
+ " type=0x" + Integer.toHexString(ev.type)
+ " scancode=" + ev.scancode
+ " keycode=" + ev.keycode
+ " value=" + ev.value);
}
if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
synchronized (mFirst) {
di = newInputDevice(ev.deviceId);
if (di.classes != 0) {
// If this device is some kind of input class,
// we care about it.
mDevices.put(ev.deviceId, di);
if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
readVirtualKeys(di.name);
}
// The configuration may have changed because
// of this device.
configChanged = true;
} else {
// We won't do anything with this device.
mIgnoredDevices.put(ev.deviceId, di);
Log.i(TAG, "Ignoring non-input device: id=0x"
+ Integer.toHexString(di.id)
+ ", name=" + di.name);
}
}
} else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
synchronized (mFirst) {
if (false) {
Log.i(TAG, "Device removed: id=0x"
+ Integer.toHexString(ev.deviceId));
}
di = mDevices.get(ev.deviceId);
if (di != null) {
mDevices.delete(ev.deviceId);
// The configuration may have changed because
// of this device.
configChanged = true;
} else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) {
mIgnoredDevices.remove(ev.deviceId);
} else {
Log.w(TAG, "Removing bad device id: "
+ Integer.toHexString(ev.deviceId));
continue;
}
}
} else {
di = getInputDevice(ev.deviceId);
if (di == null) {
// This may be some junk from an ignored device.
continue;
}
// first crack at it
send = preprocessEvent(di, ev);
if (ev.type == RawInputEvent.EV_KEY) {
di.mMetaKeysState = makeMetaState(ev.keycode,
ev.value != 0, di.mMetaKeysState);
mHaveGlobalMetaState = false;
}
}
if (configChanged) {
synchronized (mFirst) {
addLocked(di, System.nanoTime(), 0,
RawInputEvent.CLASS_CONFIGURATION_CHANGED,
null);
}
}
if (!send) {
continue;
}
synchronized (mFirst) {
// NOTE: The event timebase absolutely must be the same
// timebase as SystemClock.uptimeMillis().
//curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
final long curTime = SystemClock.uptimeMillis();
final long curTimeNano = System.nanoTime();
//Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
final int classes = di.classes;
final int type = ev.type;
final int scancode = ev.scancode;
send = false;
// Is it a key event?
if (type == RawInputEvent.EV_KEY &&
(classes&RawInputEvent.CLASS_KEYBOARD) != 0 &&
(scancode < RawInputEvent.BTN_FIRST ||
scancode > RawInputEvent.BTN_LAST)) {
boolean down;
if (ev.value != 0) {
down = true;
di.mKeyDownTime = curTime;
} else {
down = false;
}
int keycode = rotateKeyCodeLocked(ev.keycode);
addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_KEYBOARD,
newKeyEvent(di, di.mKeyDownTime, curTime, down,
keycode, 0, scancode,
((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
? KeyEvent.FLAG_WOKE_HERE : 0));
} else if (ev.type == RawInputEvent.EV_KEY) {
// Single touch protocol: touch going down or up.
if (ev.scancode == RawInputEvent.BTN_TOUCH &&
(classes&(RawInputEvent.CLASS_TOUCHSCREEN
|RawInputEvent.CLASS_TOUCHSCREEN_MT))
== RawInputEvent.CLASS_TOUCHSCREEN) {
di.mAbs.changed = true;
di.mAbs.mDown[0] = ev.value != 0;
// Trackball (mouse) protocol: press down or up.
} else if (ev.scancode == RawInputEvent.BTN_MOUSE &&
(classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
di.mRel.changed = true;
di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0;
send = true;
}
// Process position events from multitouch protocol.
} else if (ev.type == RawInputEvent.EV_ABS &&
(classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) {
di.mAbs.changed = true;
di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
+ MotionEvent.SAMPLE_PRESSURE] = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) {
di.mAbs.changed = true;
di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
+ MotionEvent.SAMPLE_X] = ev.value;
if (DEBUG_POINTERS) Log.v(TAG, "MT @"
+ di.mAbs.mAddingPointerOffset
+ " X:" + ev.value);
} else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) {
di.mAbs.changed = true;
di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
+ MotionEvent.SAMPLE_Y] = ev.value;
if (DEBUG_POINTERS) Log.v(TAG, "MT @"
+ di.mAbs.mAddingPointerOffset
+ " Y:" + ev.value);
} else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) {
di.mAbs.changed = true;
di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
+ MotionEvent.SAMPLE_SIZE] = ev.value;
}
// Process position events from single touch protocol.
} else if (ev.type == RawInputEvent.EV_ABS &&
(classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
if (ev.scancode == RawInputEvent.ABS_X) {
di.mAbs.changed = true;
di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_Y) {
di.mAbs.changed = true;
di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
di.mAbs.changed = true;
di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value;
di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
+ MotionEvent.SAMPLE_PRESSURE] = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
di.mAbs.changed = true;
di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value;
di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
+ MotionEvent.SAMPLE_SIZE] = ev.value;
}
// Process movement events from trackball (mouse) protocol.
} else if (ev.type == RawInputEvent.EV_REL &&
(classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
// Add this relative movement into our totals.
if (ev.scancode == RawInputEvent.REL_X) {
di.mRel.changed = true;
di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value;
} else if (ev.scancode == RawInputEvent.REL_Y) {
di.mRel.changed = true;
di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value;
}
}
// Handle multitouch protocol sync: tells us that the
// driver has returned all data for -one- of the pointers
// that is currently down.
if (ev.type == RawInputEvent.EV_SYN
&& ev.scancode == RawInputEvent.SYN_MT_REPORT
&& di.mAbs != null) {
di.mAbs.changed = true;
if (di.mAbs.mNextData[MotionEvent.SAMPLE_PRESSURE] > 0) {
// If the value is <= 0, the pointer is not
// down, so keep it in the count.
if (di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
+ MotionEvent.SAMPLE_PRESSURE] != 0) {
final int num = di.mAbs.mNextNumPointers+1;
di.mAbs.mNextNumPointers = num;
if (DEBUG_POINTERS) Log.v(TAG,
"MT_REPORT: now have " + num + " pointers");
final int newOffset = (num <= InputDevice.MAX_POINTERS)
? (num * MotionEvent.NUM_SAMPLE_DATA)
: (InputDevice.MAX_POINTERS *
MotionEvent.NUM_SAMPLE_DATA);
di.mAbs.mAddingPointerOffset = newOffset;
di.mAbs.mNextData[newOffset
+ MotionEvent.SAMPLE_PRESSURE] = 0;
} else {
if (DEBUG_POINTERS) Log.v(TAG, "MT_REPORT: no pointer");
}
}
// Handle general event sync: all data for the current
// event update has been delivered.
} else if (send || (ev.type == RawInputEvent.EV_SYN
&& ev.scancode == RawInputEvent.SYN_REPORT)) {
if (mDisplay != null) {
if (!mHaveGlobalMetaState) {
computeGlobalMetaStateLocked();
}
MotionEvent me;
InputDevice.MotionState ms = di.mAbs;
if (ms.changed) {
ms.changed = false;
if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN
|RawInputEvent.CLASS_TOUCHSCREEN_MT))
== RawInputEvent.CLASS_TOUCHSCREEN) {
ms.mNextNumPointers = 0;
if (ms.mDown[0]) {
System.arraycopy(di.curTouchVals, 0,
ms.mNextData, 0,
MotionEvent.NUM_SAMPLE_DATA);
ms.mNextNumPointers++;
}
}
if (BAD_TOUCH_HACK) {
ms.dropBadPoint(di);
}
boolean doMotion = !monitorVirtualKey(di,
ev, curTime, curTimeNano);
if (doMotion && ms.mNextNumPointers > 0
&& (ms.mLastNumPointers == 0
|| ms.mSkipLastPointers)) {
doMotion = !generateVirtualKeyDown(di,
ev, curTime, curTimeNano);
}
if (doMotion) {
// XXX Need to be able to generate
// multiple events here, for example
// if two fingers change up/down state
// at the same time.
do {
me = ms.generateAbsMotion(di, curTime,
curTimeNano, mDisplay,
mOrientation, mGlobalMetaState);
if (DEBUG_POINTERS) Log.v(TAG, "Absolute: x="
+ di.mAbs.mNextData[MotionEvent.SAMPLE_X]
+ " y="
+ di.mAbs.mNextData[MotionEvent.SAMPLE_Y]
+ " ev=" + me);
if (me != null) {
if (WindowManagerPolicy.WATCH_POINTER) {
Log.i(TAG, "Enqueueing: " + me);
}
addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_TOUCHSCREEN, me);
}
} while (ms.hasMore());
} else {
// We are consuming movement in the
// virtual key area... but still
// propagate this to the previous
// data for comparisons.
int num = ms.mNextNumPointers;
if (num > InputDevice.MAX_POINTERS) {
num = InputDevice.MAX_POINTERS;
}
System.arraycopy(ms.mNextData, 0,
ms.mLastData, 0,
num * MotionEvent.NUM_SAMPLE_DATA);
ms.mLastNumPointers = num;
ms.mSkipLastPointers = true;
}
ms.finish();
}
ms = di.mRel;
if (ms.changed) {
ms.changed = false;
me = ms.generateRelMotion(di, curTime,
curTimeNano,
mOrientation, mGlobalMetaState);
if (false) Log.v(TAG, "Relative: x="
+ di.mRel.mNextData[MotionEvent.SAMPLE_X]
+ " y="
+ di.mRel.mNextData[MotionEvent.SAMPLE_Y]
+ " ev=" + me);
if (me != null) {
addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_TRACKBALL, me);
}
ms.finish();
}
}
}
}
} catch (RuntimeException exc) {
Log.e(TAG, "InputReaderThread uncaught exception", exc);
}
}
}
};
private static native boolean readEvent(RawInputEvent outEvent);
framworks.base.server.jni
static jboolean
android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,
jobject event)
{
gLock.lock();
sp<EventHub> hub = gHub;
if (hub == NULL) {
hub = new EventHub;
gHub = hub;
}
gLock.unlock();
int32_t deviceId;
int32_t type;
int32_t scancode, keycode;
uint32_t flags;
int32_t value;
nsecs_t when;
bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,
&flags, &value, &when);
env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId);
env->SetIntField(event, gInputOffsets.mType, (jint)type);
env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode);
env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode);
env->SetIntField(event, gInputOffsets.mFlags, (jint)flags);
env->SetIntField(event, gInputOffsets.mValue, value);
env->SetLongField(event, gInputOffsets.mWhen,
(jlong)(nanoseconds_to_milliseconds(when)));
return res;
}
InputDispatcherThread从KeyQ中读取Events,找到Window Manager中的Focus Window,通过Focus Window记录的mClient接口,将Events专递到Client端。Client端在根据自己的Focus Path传递事件,直到事件被处理。
private final class InputDispatcherThread extends Thread {
// Time to wait when there is nothing to do: 9999 seconds.
static final int LONG_WAIT=9999*1000;
public InputDispatcherThread() {
super("InputDispatcher");
}
@Override
public void run() {
while (true) {
try {
process();
} catch (Exception e) {
Log.e(TAG, "Exception in input dispatcher", e);
}
}
}
private void process() {
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
// The last key event we saw
KeyEvent lastKey = null;
// Last keydown time for auto-repeating keys
long lastKeyTime = SystemClock.uptimeMillis();
long nextKeyTime = lastKeyTime+LONG_WAIT;
long downTime = 0;
// How many successive repeats we generated
int keyRepeatCount = 0;
// Need to report that configuration has changed?
boolean configChanged = false;
while (true) {
long curTime = SystemClock.uptimeMillis();
if (DEBUG_INPUT) Log.v(
TAG, "Waiting for next key: now=" + curTime
+ ", repeat @ " + nextKeyTime);
// Retrieve next event, waiting only as long as the next
// repeat timeout. If the configuration has changed, then
// don't wait at all -- we'll report the change as soon as
// we have processed all events.
QueuedEvent ev = mQueue.getEvent(
(int)((!configChanged && curTime < nextKeyTime)
? (nextKeyTime-curTime) : 0));
if (DEBUG_INPUT && ev != null) Log.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()) {
// cancel key repeat at the request of the policy.
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) {
// Ignore
}
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();
//a21966,Creekside: if it is a SLIDER close event do not wait the key up event
if (ke.getScanCode() == 254){
lastKey = null;
downTime = 0;
lastKeyTime = curTime;
nextKeyTime = curTime + LONG_WAIT;
}
if (DEBUG_INPUT) Log.v(
TAG, "Received key down: first repeat @ "
+ nextKeyTime);
} else {
lastKey = null;
downTime = 0;
// Arbitrary long timeout.
lastKeyTime = curTime;
nextKeyTime = curTime + LONG_WAIT;
if (DEBUG_INPUT) Log.v(
TAG, "Received key up: ignore repeat @ "
+ nextKeyTime);
}
dispatchKey((KeyEvent)ev.event, 0, 0);
mQueue.recycleEvent(ev);
break;
case RawInputEvent.CLASS_TOUCHSCREEN:
//Log.i(TAG, "Read next event " + ev);
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();
// Timeout occurred while key was down. If it is at or
// past the key repeat time, dispatch the repeat.
if (DEBUG_INPUT) Log.v(
TAG, "Key timeout: repeat=" + nextKeyTime
+ ", now=" + curTime);
if (curTime < nextKeyTime) {
continue;
}
lastKeyTime = nextKeyTime;
nextKeyTime = nextKeyTime + KEY_REPEAT_DELAY;
keyRepeatCount++;
if (DEBUG_INPUT) Log.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) {
Log.e(TAG,
"Input thread received uncaught exception: " + e, e);
}
}
}
}