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); } } } }