分析完事件的读取后,东忙西忙,不知不觉已过去了快五个月了…也不是说没有时间把这部分源码分析完,而是实在是分析不下去,因此转战到其他地方去了。然而这一块始终是心头那不舍的惦记,经过一段时间的沉淀,又参考了网上很多大神的文章,然后再来阅读源码,渐渐感觉到能看出点眉目了。因而事不宜迟,赶紧做个记录吧(注:分析使用的Android源码版本为6.0)。
前面两篇文章分析输入事件的读取,通过分析,发现时间的读取是在EventHub类中实现的,EventHub类的getEevent方法中使用epoll_wait来监听驱动程序上报的事件,而在InputReaderThread的threadLoop方法中调用InputReader的loopOnce方法来不断的调用EventHub的getEvent方法来一直监听事件的到来,getEvent是一个阻塞的方法,当没有事件的时候,epoll_wait方法就会是线程休眠,有事件了就会唤醒线程。

从事件读取线程到事件分发线程的转移过程

既然有个InputReaderThread线程,那么有个InputDispatcherThread就不奇怪了,一个用来读取,一个用来分发,分工合作,相互配合,这样才能高效的完成事件的读取与分发。
我们先梳理下思路:

从事件读取线程到事件分发线程的转移时序图:

从时序图中我们可以看到两个线程交互的过程。InputReaderThread会唤醒InputDispatcherThread来分发事件。
接下来,我们通过源码的追踪的方式,具体看看时序图中涉及的代码的具体实现。

跟着时序图,我们代码从InputReaderThread开始看起:

InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :Thread(/*canCallJava*/ true), mReader(reader) {
}InputReaderThread::~InputReaderThread() {
}bool InputReaderThread::threadLoop() {mReader->loopOnce();return true;
}

构造函数和析构函数都是空的,循环函数简单的就一句。mReader就是当前的InputReader对象,因此loopOnce如下:

void InputReader::loopOnce() {int32_t oldGeneration;int32_t timeoutMillis;bool inputDevicesChanged = false;Vector<InputDeviceInfo> inputDevices;{ // acquire lockAutoMutex _l(mLock);oldGeneration = mGeneration;timeoutMillis = -1;...//获取事件size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);{ // acquire lockAutoMutex _l(mLock);mReaderIsAliveCondition.broadcast();if (count) {//处理事件processEventsLocked(mEventBuffer, count);}...if (oldGeneration != mGeneration) {inputDevicesChanged = true;getInputDevicesLocked(inputDevices);}} // release lock// Send out a message that the describes the changed input devices.if (inputDevicesChanged) {mPolicy->notifyInputDevicesChanged(inputDevices);}...mQueuedListener->flush();
}

只列出了比较重要的一些代码。这个函数作如下事情:
1.使用mEventHub->getEvents获取输入事件
2.使用processEventsLocked处理事件
getEvents方法之前也已经有介绍过,它就是使用epoll来监听驱动程序上报的事件的。所以我们从processEventsLocked方法看起:

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {for (const RawEvent* rawEvent = rawEvents; count;) {int32_t type = rawEvent->type;size_t batchSize = 1;if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {int32_t deviceId = rawEvent->deviceId;while (batchSize < count) {if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT|| rawEvent[batchSize].deviceId != deviceId) {break;}batchSize += 1;}
#if DEBUG_RAW_EVENTSALOGD("BatchSize: %d Count: %d", batchSize, count);
#endifprocessEventsForDeviceLocked(deviceId, rawEvent, batchSize);} else {switch (rawEvent->type) {case EventHubInterface::DEVICE_ADDED:addDeviceLocked(rawEvent->when, rawEvent->deviceId);break;case EventHubInterface::DEVICE_REMOVED:removeDeviceLocked(rawEvent->when, rawEvent->deviceId);break;case EventHubInterface::FINISHED_DEVICE_SCAN:handleConfigurationChangedLocked(rawEvent->when);break;default:ALOG_ASSERT(false); // can't happenbreak;}}count -= batchSize;rawEvent += batchSize;}
}

这个方法会对事件类型进行判断。我们假定用户按了一下返回按键,所以输入的是一个按键事件。而这个函数判断的时间类型的定义如下:

    enum {// Sent when a device is added.DEVICE_ADDED = 0x10000000,// Sent when a device is removed.DEVICE_REMOVED = 0x20000000,// Sent when all added/removed devices from the most recent scan have been reported.// This event is always sent at least once.FINISHED_DEVICE_SCAN = 0x30000000,FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,};

可见我们的按键事件绝不是这几个,而应该是小于FIRST_SYNTHETIC_EVENT事件的,因此会执行第一个分支。进而调用 processEventsForDeviceLocked(deviceId, rawEvent, batchSize);方法进一步处理:

void InputReader::processEventsForDeviceLocked(int32_t deviceId,const RawEvent* rawEvents, size_t count) {ssize_t deviceIndex = mDevices.indexOfKey(deviceId);if (deviceIndex < 0) {ALOGW("Discarding event for unknown deviceId %d.", deviceId);return;}InputDevice* device = mDevices.valueAt(deviceIndex);if (device->isIgnored()) {//ALOGD("Discarding event for ignored deviceId %d.", deviceId);return;}device->process(rawEvents, count);
}

调用device->process因一部处理,device是InputDevice的实例,因此看看InputDevice下的process方法:

void InputDevice::process(const RawEvent* rawEvents, size_t count) {// Process all of the events in order for each mapper.// We cannot simply ask each mapper to process them in bulk because mappers may// have side-effects that must be interleaved.  For example, joystick movement events and// gamepad button presses are handled by different mappers but they should be dispatched// in the order received.size_t numMappers = mMappers.size();for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
#if DEBUG_RAW_EVENTSALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld",rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,rawEvent->when);
#endifif (mDropUntilNextSync) {if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {mDropUntilNextSync = false;
#if DEBUG_RAW_EVENTSALOGD("Recovered from input event buffer overrun.");
#endif} else {
#if DEBUG_RAW_EVENTSALOGD("Dropped input event while waiting for next input sync.");
#endif}} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {ALOGI("Detected input event buffer overrun for device %s.", getName().string());mDropUntilNextSync = true;reset(rawEvent->when);} else {for (size_t i = 0; i < numMappers; i++) {InputMapper* mapper = mMappers[i];mapper->process(rawEvent);}}}
}

为了更好的理解这段代码,我们列出事件类型的定义:


/* Events */#define EV_SYN          0x00
#define EV_KEY          0x01
#define EV_REL          0x02
#define EV_ABS          0x03
#define EV_MSC          0x04
#define EV_LED          0x11
#define EV_SND          0x12
#define EV_REP          0x14
#define EV_FF           0x15
#define EV_PWR          0x16
#define EV_FF_STATUS        0x17
#define EV_MAX          0x1f

因此我们的按键时间应该是0x01,所以这段代码执行的下面这个for循环:

            for (size_t i = 0; i < numMappers; i++) {InputMapper* mapper = mMappers[i];mapper->process(rawEvent);}

for循环遍历mMappers数组,分别调用每一个mapper的process方法。这个数组包含了所有的输入事件的类型。我们按下的按键,因此会调用按键类型的Mapper来处理。如果想搞清楚这一块,可以看下mMappers数组的构造过程,在createDeviceLocked方法中实现,

InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,const InputDeviceIdentifier& identifier, uint32_t classes) {InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),controllerNumber, identifier, classes);// External devices.if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {device->setExternal(true);}// Devices with mics.if (classes & INPUT_DEVICE_CLASS_MIC) {device->setMic(true);}// Switch-like devices.if (classes & INPUT_DEVICE_CLASS_SWITCH) {device->addMapper(new SwitchInputMapper(device));}// Vibrator-like devices.if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {device->addMapper(new VibratorInputMapper(device));}// Keyboard-like devices.uint32_t keyboardSource = 0;int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {keyboardSource |= AINPUT_SOURCE_KEYBOARD;}if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;}if (classes & INPUT_DEVICE_CLASS_DPAD) {keyboardSource |= AINPUT_SOURCE_DPAD;}if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {keyboardSource |= AINPUT_SOURCE_GAMEPAD;}if (keyboardSource != 0) {device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));}// Cursor-like devices.if (classes & INPUT_DEVICE_CLASS_CURSOR) {device->addMapper(new CursorInputMapper(device));}// Touchscreens and touchpad devices.if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {device->addMapper(new MultiTouchInputMapper(device));} else if (classes & INPUT_DEVICE_CLASS_TOUCH) {device->addMapper(new SingleTouchInputMapper(device));}// Joystick-like devices.if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {device->addMapper(new JoystickInputMapper(device));}// External stylus-like devices.if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {device->addMapper(new ExternalStylusInputMapper(device));}return device;
}

因为我们事件是按键事件,所以,其他的mapper的process什么都不做,最终是KeyboardInputMapper的process方法。

void KeyboardInputMapper::process(const RawEvent* rawEvent) {switch (rawEvent->type) {case EV_KEY: {int32_t scanCode = rawEvent->code;int32_t usageCode = mCurrentHidUsage;mCurrentHidUsage = 0;if (isKeyboardOrGamepadKey(scanCode)) {int32_t keyCode;uint32_t flags;if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {keyCode = AKEYCODE_UNKNOWN;flags = 0;}processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);}break;}case EV_MSC: {if (rawEvent->code == MSC_SCAN) {mCurrentHidUsage = rawEvent->value;}break;}case EV_SYN: {if (rawEvent->code == SYN_REPORT) {mCurrentHidUsage = 0;}}}
}

传入的事件类型为按键事件,因此会调用processKey方法进一步处理:

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,int32_t scanCode, uint32_t policyFlags) {if (down) {// Rotate key codes according to orientation if needed.if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) {keyCode = rotateKeyCode(keyCode, mOrientation);}// Add key down.ssize_t keyDownIndex = findKeyDown(scanCode);if (keyDownIndex >= 0) {// key repeat, be sure to use same keycode as before in case of rotationkeyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;} else {// key downif ((policyFlags & POLICY_FLAG_VIRTUAL)&& mContext->shouldDropVirtualKey(when,getDevice(), keyCode, scanCode)) {return;}if (policyFlags & POLICY_FLAG_GESTURE) {mDevice->cancelTouch(when);}mKeyDowns.push();KeyDown& keyDown = mKeyDowns.editTop();keyDown.keyCode = keyCode;keyDown.scanCode = scanCode;}mDownTime = when;} else {// Remove key down.ssize_t keyDownIndex = findKeyDown(scanCode);if (keyDownIndex >= 0) {// key up, be sure to use same keycode as before in case of rotationkeyCode = mKeyDowns.itemAt(keyDownIndex).keyCode;mKeyDowns.removeAt(size_t(keyDownIndex));} else {// key was not actually downALOGI("Dropping key up from device %s because the key was not down.  ""keyCode=%d, scanCode=%d",getDeviceName().string(), keyCode, scanCode);return;}}int32_t oldMetaState = mMetaState;int32_t newMetaState = updateMetaState(keyCode, down, oldMetaState);bool metaStateChanged = oldMetaState != newMetaState;if (metaStateChanged) {mMetaState = newMetaState;updateLedState(false);}nsecs_t downTime = mDownTime;// Key down on external an keyboard should wake the device.// We don't do this for internal keyboards to prevent them from waking up in your pocket.// For internal keyboards, the key layout file should specify the policy flags for// each wake key individually.// TODO: Use the input device configuration to control this behavior more finely.if (down && getDevice()->isExternal()) {
// MStar Android Patch Begin
#ifdef ENABLE_STRchar value[PROPERTY_VALUE_MAX];property_get("mstar.str.suspending", value, "0");if (atoi(value) == 0) {policyFlags |= POLICY_FLAG_WAKE;}
#elsepolicyFlags |= POLICY_FLAG_WAKE;
#endif
// MStar Android Patch End}if (mParameters.handlesKeyRepeat) {policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;}if (metaStateChanged) {getContext()->updateGlobalMetaState();}if (down && !isMetaKey(keyCode)) {getContext()->fadePointer();}NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);getListener()->notifyKey(&args);
}

由于能力有限,我无法关注太多的细节,这个方法的最后调用了 getListener()->notifyKey(&args);其实就是调用了InputDispatcher的notifyKey方法。为什么这么说呢?这个方法展开看一下就明白了。

InputListenerInterface* InputReader::ContextImpl::getListener() {return mReader->mQueuedListener.get();
}

InpurReader的构造函数中创建了mQueuedListener变量。

InputReader::InputReader(const sp<EventHubInterface>& eventHub,const sp<InputReaderPolicyInterface>& policy,const sp<InputListenerInterface>& listener) :mContext(this), mEventHub(eventHub), mPolicy(policy),mGlobalMetaState(0), mGeneration(1),mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),mConfigurationChangesToRefresh(0) {mQueuedListener = new QueuedInputListener(listener);{ // acquire lockAutoMutex _l(mLock);refreshConfigurationLocked(0);updateGlobalMetaStateLocked();} // release lock
}

QueuedInputListener构造函数中传入了一个listener,这个listener是不是InputDispatcher呢?
我们之前就分析过,InputReader是在InputManager中创建的,看看InputManager得构造函数:

InputManager::InputManager(const sp<EventHubInterface>& eventHub,const sp<InputReaderPolicyInterface>& readerPolicy,const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {mDispatcher = new InputDispatcher(dispatcherPolicy);mReader = new InputReader(eventHub, readerPolicy, mDispatcher);initialize();
}

真相大白了,listener就是mDispatcher。mDispatcher是InputDispatcher的实例。
因此,下一步就进入到了InputDispatcher的notifyKey方法了:

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILSALOGD("notifyKey - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, action=0x%x, ""flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld",args->eventTime, args->deviceId, args->source, args->policyFlags,args->action, args->flags, args->keyCode, args->scanCode,args->metaState, args->downTime);
#endifif (!validateKeyEvent(args->action)) {return;}uint32_t policyFlags = args->policyFlags;int32_t flags = args->flags;int32_t metaState = args->metaState;if ((policyFlags & POLICY_FLAG_VIRTUAL) || (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY)) {policyFlags |= POLICY_FLAG_VIRTUAL;flags |= AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY;}if (policyFlags & POLICY_FLAG_FUNCTION) {metaState |= AMETA_FUNCTION_ON;}policyFlags |= POLICY_FLAG_TRUSTED;int32_t keyCode = args->keyCode;if (metaState & AMETA_META_ON && args->action == AKEY_EVENT_ACTION_DOWN) {int32_t newKeyCode = AKEYCODE_UNKNOWN;if (keyCode == AKEYCODE_DEL) {newKeyCode = AKEYCODE_BACK;} else if (keyCode == AKEYCODE_ENTER) {newKeyCode = AKEYCODE_HOME;}if (newKeyCode != AKEYCODE_UNKNOWN) {AutoMutex _l(mLock);struct KeyReplacement replacement = {keyCode, args->deviceId};mReplacedKeys.add(replacement, newKeyCode);keyCode = newKeyCode;metaState &= ~AMETA_META_ON;}} else if (args->action == AKEY_EVENT_ACTION_UP) {// In order to maintain a consistent stream of up and down events, check to see if the key// going up is one we've replaced in a down event and haven't yet replaced in an up event,// even if the modifier was released between the down and the up events.AutoMutex _l(mLock);struct KeyReplacement replacement = {keyCode, args->deviceId};ssize_t index = mReplacedKeys.indexOfKey(replacement);if (index >= 0) {keyCode = mReplacedKeys.valueAt(index);mReplacedKeys.removeItemsAt(index);metaState &= ~AMETA_META_ON;}}KeyEvent event;event.initialize(args->deviceId, args->source, args->action,flags, keyCode, args->scanCode, metaState, 0,args->downTime, args->eventTime);mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);bool needWake;{ // acquire lockmLock.lock();if (shouldSendKeyToInputFilterLocked(args)) {mLock.unlock();policyFlags |= POLICY_FLAG_FILTERED;if (!mPolicy->filterInputEvent(&event, policyFlags)) {return; // event was consumed by the filter}mLock.lock();}int32_t repeatCount = 0;KeyEntry* newEntry = new KeyEntry(args->eventTime,args->deviceId, args->source, policyFlags,args->action, flags, keyCode, args->scanCode,metaState, repeatCount, args->downTime);needWake = enqueueInboundEventLocked(newEntry);mLock.unlock();} // release lockif (needWake) {mLooper->wake();}
}

这份函数做了三件事情:
1.把时间封装成了KeyEvent类型的对象。
2.拦截事件。有些事件是必须要拦截的,这类事件不需要发送到window中去,比如关机键,需要系统来处理。拦截的主要代码:

 mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);

这部分也很重要,但是我们暂不展开,先知道这里拦截了事件,因为这里的拦截最终会调用PhoneWindowManager的interceptKeyBeforeQueueing等方法,解开了我PhoneWindowManager中相关方法什么时候调用的不清楚的迷惑。我们先放一放,之后在展开。
3.唤醒InputDispatcherThread线程:

 mLooper->wake();

这个线程唤醒意味着事件的分发终于转入到InputDispatcherThread了,InputReaderThread线程完成了它的使命。为什么这行代码能唤醒InputDispatcherThread呢?这还的从InputDispatcherThread说起:

InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) :Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {
}InputDispatcherThread::~InputDispatcherThread() {
}bool InputDispatcherThread::threadLoop() {mDispatcher->dispatchOnce();return true;
}

这个线程和InputReaderThread线程类似,调用InputDispatcher的dispatchOnce做一次事件分发:

void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime = LONG_LONG_MAX;{ // acquire lockAutoMutex _l(mLock);mDispatcherIsAliveCondition.broadcast();// Run a dispatch loop if there are no pending commands.// The dispatch loop might enqueue commands to run afterwards.if (!haveCommandsLocked()) {dispatchOnceInnerLocked(&nextWakeupTime);}// Run all pending commands if there are any.// If any commands were run then force the next poll to wake up immediately.if (runCommandsLockedInterruptible()) {nextWakeupTime = LONG_LONG_MIN;}} // release lock// Wait for callback or timeout or wake.  (make sure we round up, not down)nsecs_t currentTime = now();int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);mLooper->pollOnce(timeoutMillis);
}

可以看到它内部使用Looper,调用mLooper->pollOnce(timeoutMillis)后,如果没有数据可以读取,会导致线程休眠,调用mLooper->wake可以唤醒它,唤醒后dispatchOnceInnerLocked来分发事件。至此,我们绘制的事件读取线程到事件分发线程的转移时序图就走到了尽头。下一部分到底是怎么把分发到window的呢?于是我们进入到了事件发送的第二个阶段。

InputDispatcherThread中的事件发送

我们先看一下这个阶段的时序图:

事件的发送和接收一定是你情我愿的事情,强求不来的。但是,我们目前已知在分析事件的发送,没有关注事件的接收端,因此,真正的发送流程中有很多东西理解不来,不过没关系,我们先理清发送的流程,等我们理解了事件的接收端以后,再反过头来看这部分就豁然开朗了。因此,这之后的代码我们主要是追踪事件发送的流程,而不苛求一定要理解透彻这部分代码。
我们从上一个时序图结束的位置开始追踪吧,dispatchOnceInnerLocked函数定义如下:

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {nsecs_t currentTime = now();// Reset the key repeat timer whenever normal dispatch is suspended while the// device is in a non-interactive state.  This is to ensure that we abort a key// repeat if the device is just coming out of sleep.if (!mDispatchEnabled) {resetKeyRepeatLocked();}// If dispatching is frozen, do not process timeouts or try to deliver any new events.if (mDispatchFrozen) {
#if DEBUG_FOCUSALOGD("Dispatch frozen.  Waiting some more.");
#endifreturn;}// Optimize latency of app switches.// Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has// been pressed.  When it expires, we preempt dispatch and drop all other pending events.bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;if (mAppSwitchDueTime < *nextWakeupTime) {*nextWakeupTime = mAppSwitchDueTime;}// Ready to start a new event.// If we don't already have a pending event, go grab one.if (! mPendingEvent) {if (mInboundQueue.isEmpty()) {if (isAppSwitchDue) {// The inbound queue is empty so the app switch key we were waiting// for will never arrive.  Stop waiting for it.resetPendingAppSwitchLocked(false);isAppSwitchDue = false;}// Synthesize a key repeat if appropriate.if (mKeyRepeatState.lastKeyEntry) {if (currentTime >= mKeyRepeatState.nextRepeatTime) {mPendingEvent = synthesizeKeyRepeatLocked(currentTime);} else {if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {*nextWakeupTime = mKeyRepeatState.nextRepeatTime;}}}// Nothing to do if there is no pending event.if (!mPendingEvent) {return;}} else {// Inbound queue has at least one entry.mPendingEvent = mInboundQueue.dequeueAtHead();traceInboundQueueLengthLocked();}// Poke user activity for this event.if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {pokeUserActivityLocked(mPendingEvent);}// Get ready to dispatch the event.resetANRTimeoutsLocked();}// Now we have an event to dispatch.// All events are eventually dequeued and processed this way, even if we intend to drop them.ALOG_ASSERT(mPendingEvent != NULL);bool done = false;DropReason dropReason = DROP_REASON_NOT_DROPPED;if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) {dropReason = DROP_REASON_POLICY;} else if (!mDispatchEnabled) {dropReason = DROP_REASON_DISABLED;}if (mNextUnblockedEvent == mPendingEvent) {mNextUnblockedEvent = NULL;}switch (mPendingEvent->type) {case EventEntry::TYPE_CONFIGURATION_CHANGED: {ConfigurationChangedEntry* typedEntry =static_cast<ConfigurationChangedEntry*>(mPendingEvent);done = dispatchConfigurationChangedLocked(currentTime, typedEntry);dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never droppedbreak;}case EventEntry::TYPE_DEVICE_RESET: {DeviceResetEntry* typedEntry =static_cast<DeviceResetEntry*>(mPendingEvent);done = dispatchDeviceResetLocked(currentTime, typedEntry);dropReason = DROP_REASON_NOT_DROPPED; // device resets are never droppedbreak;}case EventEntry::TYPE_KEY: {KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);if (isAppSwitchDue) {if (isAppSwitchKeyEventLocked(typedEntry)) {resetPendingAppSwitchLocked(true);isAppSwitchDue = false;} else if (dropReason == DROP_REASON_NOT_DROPPED) {dropReason = DROP_REASON_APP_SWITCH;}}if (dropReason == DROP_REASON_NOT_DROPPED&& isStaleEventLocked(currentTime, typedEntry)) {dropReason = DROP_REASON_STALE;}if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {dropReason = DROP_REASON_BLOCKED;}done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);break;}case EventEntry::TYPE_MOTION: {MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {dropReason = DROP_REASON_APP_SWITCH;}if (dropReason == DROP_REASON_NOT_DROPPED&& isStaleEventLocked(currentTime, typedEntry)) {dropReason = DROP_REASON_STALE;}if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {dropReason = DROP_REASON_BLOCKED;}done = dispatchMotionLocked(currentTime, typedEntry,&dropReason, nextWakeupTime);break;}default:ALOG_ASSERT(false);break;}if (done) {if (dropReason != DROP_REASON_NOT_DROPPED) {dropInboundEventLocked(mPendingEvent, dropReason);}mLastDropReason = dropReason;releasePendingEventLocked();*nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately}
}

我们的时间类型为TYPE_KEY,所以会调用到dispatchKeyLocked方法:

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,DropReason* dropReason, nsecs_t* nextWakeupTime) {// Preprocessing.
...// Dispatch the key.dispatchEventLocked(currentTime, entry, inputTargets);return true;
}

省略了很多类容。我的目的是理清框架,所以想理解细节的就请绕道吧。再看看dispatchEventLocked方法的实现:

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
#if DEBUG_DISPATCH_CYCLEALOGD("dispatchEventToCurrentInputTargets");
#endifALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to truepokeUserActivityLocked(eventEntry);for (size_t i = 0; i < inputTargets.size(); i++) {const InputTarget& inputTarget = inputTargets.itemAt(i);ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);if (connectionIndex >= 0) {sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);} else {
#if DEBUG_FOCUSALOGD("Dropping event delivery to target with channel '%s' because it ""is no longer registered with the input dispatcher.",inputTarget.inputChannel->getName().string());
#endif}}
}

调用prepareDispatchCycleLocked方法进一步处理:

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {...// Skip this event if the connection status is not normal.// We don't want to enqueue additional outbound events if the connection is broken.if (connection->status != Connection::STATUS_NORMAL) {return;}// Split a motion event if needed.if (inputTarget->flags & InputTarget::FLAG_SPLIT) {ALOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION);MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry);if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) {MotionEntry* splitMotionEntry = splitMotionEvent(originalMotionEntry, inputTarget->pointerIds);if (!splitMotionEntry) {return; // split event was dropped}
#if DEBUG_FOCUSALOGD("channel '%s' ~ Split motion event.",connection->getInputChannelName());logOutboundMotionDetailsLocked("  ", splitMotionEntry);
#endifenqueueDispatchEntriesLocked(currentTime, connection,splitMotionEntry, inputTarget);splitMotionEntry->release();return;}}

这个函数检查connect状态是不是正常,什么连接状态?
又调用到enqueueDispatchEntriesLocked方法了:

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {bool wasEmpty = connection->outboundQueue.isEmpty();// Enqueue dispatch entries for the requested modes.enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_OUTSIDE);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_IS);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);// If the outbound queue was previously empty, start the dispatch cycle going.if (wasEmpty && !connection->outboundQueue.isEmpty()) {startDispatchCycleLocked(currentTime, connection);}
}

舍繁求简抓骨干,继续追踪,代码进入到startDispatchCycleLocked

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,const sp<Connection>& connection) {
#if DEBUG_DISPATCH_CYCLEALOGD("channel '%s' ~ startDispatchCycle",connection->getInputChannelName());
#endifwhile (connection->status == Connection::STATUS_NORMAL&& !connection->outboundQueue.isEmpty()) {DispatchEntry* dispatchEntry = connection->outboundQueue.head;dispatchEntry->deliveryTime = currentTime;// Publish the event.status_t status;EventEntry* eventEntry = dispatchEntry->eventEntry;switch (eventEntry->type) {case EventEntry::TYPE_KEY: {KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);// Publish the key event.status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,keyEntry->deviceId, keyEntry->source,dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,keyEntry->keyCode, keyEntry->scanCode,keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,keyEntry->eventTime);break;}case EventEntry::TYPE_MOTION: {MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);PointerCoords scaledCoords[MAX_POINTERS];const PointerCoords* usingCoords = motionEntry->pointerCoords;// Set the X and Y offset depending on the input source.float xOffset, yOffset, scaleFactor;if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)&& !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {scaleFactor = dispatchEntry->scaleFactor;xOffset = dispatchEntry->xOffset * scaleFactor;yOffset = dispatchEntry->yOffset * scaleFactor;if (scaleFactor != 1.0f) {for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {scaledCoords[i] = motionEntry->pointerCoords[i];scaledCoords[i].scale(scaleFactor);}usingCoords = scaledCoords;}} else {xOffset = 0.0f;yOffset = 0.0f;scaleFactor = 1.0f;// We don't want the dispatch target to know.if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {for (uint32_t i = 0; i < motionEntry->pointerCount; i++) {scaledCoords[i].clear();}usingCoords = scaledCoords;}}// Publish the motion event.status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,motionEntry->deviceId, motionEntry->source,dispatchEntry->resolvedAction, motionEntry->actionButton,dispatchEntry->resolvedFlags, motionEntry->edgeFlags,motionEntry->metaState, motionEntry->buttonState,xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision,motionEntry->downTime, motionEntry->eventTime,motionEntry->pointerCount, motionEntry->pointerProperties,usingCoords);break;}default:ALOG_ASSERT(false);return;}// Check the result.if (status) {if (status == WOULD_BLOCK) {if (connection->waitQueue.isEmpty()) {ALOGE("channel '%s' ~ Could not publish event because the pipe is full. ""This is unexpected because the wait queue is empty, so the pipe ""should be empty and we shouldn't have any problems writing an ""event to it, status=%d", connection->getInputChannelName(), status);abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);} else {// Pipe is full and we are waiting for the app to finish process some events// before sending more events to it.
#if DEBUG_DISPATCH_CYCLEALOGD("channel '%s' ~ Could not publish event because the pipe is full, ""waiting for the application to catch up",connection->getInputChannelName());
#endifconnection->inputPublisherBlocked = true;}} else {ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, ""status=%d", connection->getInputChannelName(), status);abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/);}return;}// Re-enqueue the event on the wait queue.connection->outboundQueue.dequeue(dispatchEntry);traceOutboundQueueLengthLocked(connection);connection->waitQueue.enqueueAtTail(dispatchEntry);traceWaitQueueLengthLocked(connection);}
}

只关注我们的TYPE_KEY类型的事件:
connection->inputPublisher.publishKeyEvent:

status_t InputPublisher::publishKeyEvent(uint32_t seq,int32_t deviceId,int32_t source,int32_t action,int32_t flags,int32_t keyCode,int32_t scanCode,int32_t metaState,int32_t repeatCount,nsecs_t downTime,nsecs_t eventTime) {
#if DEBUG_TRANSPORT_ACTIONSALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, ""action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,""downTime=%lld, eventTime=%lld",mChannel->getName().string(), seq,deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,downTime, eventTime);
#endifif (!seq) {ALOGE("Attempted to publish a key event with sequence number 0.");return BAD_VALUE;}InputMessage msg;msg.header.type = InputMessage::TYPE_KEY;msg.body.key.seq = seq;msg.body.key.deviceId = deviceId;msg.body.key.source = source;msg.body.key.action = action;msg.body.key.flags = flags;msg.body.key.keyCode = keyCode;msg.body.key.scanCode = scanCode;msg.body.key.metaState = metaState;msg.body.key.repeatCount = repeatCount;msg.body.key.downTime = downTime;msg.body.key.eventTime = eventTime;return mChannel->sendMessage(&msg);
}

继续追踪代码流程:调用到了InputChannel的sendMessage方法了。

status_t InputChannel::sendMessage(const InputMessage* msg) {size_t msgLength = msg->size();ssize_t nWrite;do {nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);} while (nWrite == -1 && errno == EINTR);if (nWrite < 0) {int error = errno;
#if DEBUG_CHANNEL_MESSAGESALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(),msg->header.type, error);
#endifif (error == EAGAIN || error == EWOULDBLOCK) {return WOULD_BLOCK;}if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED || error == ECONNRESET) {return DEAD_OBJECT;}return -error;}if (size_t(nWrite) != msgLength) {
#if DEBUG_CHANNEL_MESSAGESALOGD("channel '%s' ~ error sending message type %d, send was incomplete",mName.string(), msg->header.type);
#endifreturn DEAD_OBJECT;}#if DEBUG_CHANNEL_MESSAGESALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type);
#endifreturn OK;
}

这个方法中,重点是下面这行代码:

::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);

InputChannel的本质是linux本地套接字。linux本地套接字可以用于进程间通信,InputChannel的openInputChannelPair方法中使用了socketpair函数创建了Linux本地套接字,socketpair会返回两个文件描述符,持有这两个文件描述符的进程就可以进行进程间的通信。

status_t InputChannel::openInputChannelPair(const String8& name,sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {int sockets[2];if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {status_t result = -errno;ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",name.string(), errno);outServerChannel.clear();outClientChannel.clear();return result;}int bufferSize = SOCKET_BUFFER_SIZE;setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));String8 serverChannelName = name;serverChannelName.append(" (server)");outServerChannel = new InputChannel(serverChannelName, sockets[0]);String8 clientChannelName = name;clientChannelName.append(" (client)");outClientChannel = new InputChannel(clientChannelName, sockets[1]);return OK;
}

这个方法的最后创建了两个InputChannel,分别传入一个文件描述符。因此持有这两个InputChannel实例的进程就可以做进程间的通信。
当我们调用到InputChannel的sendMessage方法时,其实已经是在和另一端通信了,另一端收到的消息就是我们的按键事件了。我们消息分发到此就到了尽头,另一端到底是谁在接收?我们需要另做分析,我不太喜欢写特别长的博客,读起来太费劲,因此,预知是谁在接收事件,请见下回分解。

Android输入事件从读取到分发三:InputDispatcherThread线程分发事件的过程相关推荐

  1. Android 系统(57)---深入理解Android输入系统

    <深入理解Android 卷III>第五章 深入理解Android输入系统 <深入理解Android 卷III>即将发布,作者是张大伟.此书填补了深入理解Android Fra ...

  2. Qt线程、事件与QObject

    线程.事件与QObject 敬告:测试版本 原文连接:http://m.blog.csdn.net/blog/shang322/9344475# 本译文接近定稿,但还须一些加工和更好的例子.欢迎任何评 ...

  3. 【EventBus】事件通信框架 ( 订阅类-订阅方法缓存集合 | 事件类型-订阅者集合 | 订阅对象-事件类型集合 )

    文章目录 前言 一.订阅类-订阅方法缓存集合 二.事件类型-订阅者集合 三.订阅对象-事件类型集合 前言 首先声明几个数据结构 , 参考 [EventBus]EventBus 源码解析 ( 注册订阅者 ...

  4. Android输入系统(三)InputReader的加工类型和InputDispatcher的分发过程

    关联系列 解析WMS系列 深入理解JNI系列 输入系统系列 前言 在上一篇文章中,我们学习了输入事件的处理,输入事件会交由InputDispatcher进行分发,那么InputDispatcher是如 ...

  5. Android输入系统(三)——InputReader的加工类型和InputDispatcher的分发过程(基于Android 13)

    1 InputReader的加工类型 InputReader具有多种加工类型,由上面的InputReader的执行流程可知,对于按键事件,InputReader会将按键信息封装成NotifyKeyAr ...

  6. Android 输入事件一撸到底之View接盘侠(3)

    前言 系列文章 1.Android 输入事件一撸到底之源头活水(1) 2.Android 输入事件一撸到底之DecorView拦路虎(2) 3.Android 输入事件一撸到底之View接盘侠(3 前 ...

  7. android 事件管理器,Android输入管理InputManager之读一次事件的流程

    流程图如下所示: 读一次事件的流程.png 读取线程InputReaderThread执行InputReader#loopOnce一次 void InputReader::loopOnce() { i ...

  8. [Android] 输入系统(三):加载按键映射

    映射表基本概念 由于Android调用getEvents得到的key是linux发送过来的scan code,而Android处理的是类似于KEY_UP这种统一类型的key code,因此需要有映射表 ...

  9. Android数据存储和读取的三种方法

    Android数据存储和读取的三种方法 一.文件存储 二.Context存储 三.SharedPreferences存储 一.文件存储 1.利用文件进行数据的存储 public static bool ...

最新文章

  1. 清华大学张悠慧:超越冯·诺依曼模型,实现软硬件去耦合的类脑计算(附视频)
  2. docker-macvlan网络
  3. 3.11 TensorFlow-深度学习第二课《改善深层神经网络》-Stanford吴恩达教授
  4. xamp已有mysql端口修改依然启动不_关于xampp启动不了mysql的解决方法
  5. 数据结构之线段树合并——永无乡,Lomsat gelral,Tree Rotations,Tree Rotations Escape Through Leaf
  6. 设计模式(十一):从文Finder中认识组合模式(Composite Pattern)
  7. 中国电信建成全球首个覆盖最广的商用下一代物联网
  8. 读《大道至简》第四章有感
  9. 一张图彻底了解Unity脚本的生命周期
  10. JavaScript学习指南 (来自转载)
  11. 计算机网络知识点全面总结(有这一篇就够了!!!)
  12. AppleScript 的一些命令
  13. 王者荣耀服务器什么时候维护结束,王者荣耀维护更新到几点 王者荣耀今天维护多久 新赛季几点维护完?...
  14. android wear tizen,三星tizen和谷歌android wear对比 android wear和三星tizen哪个好
  15. 如何读群晖硬盘_如何优雅无损的更换群晖硬盘
  16. stringsAsFactors=FALSE是什么意思
  17. topcoder 第一次比赛
  18. 整合stripe线上支付收款
  19. H5页面支持拍照选择图片
  20. String字符串转List<Map>集合

热门文章

  1. MIT新任女校长震撼北美高校圈!61岁的她曾是杜克首位女教务长
  2. [WinError 2] 系统找不到指定的文件
  3. PPT使用技巧 一 更改幻灯片版式
  4. 考研 数学1 2 3 区别
  5. 【小程序开发之制作首页】
  6. 分布式服务协调---幂等(Idempotent)机制
  7. asp实训报告摘要_ASP实训总结
  8. 手机开锁显示无法连接到服务器是什么问题,苹果手机连接服务器失败原因 苹果手机连接服务器失败是什么原因...
  9. 在线的h5编辑器汇总和分析
  10. protobuf 3.5 java使用介绍(二)