Android4.1 InputManagerService 流程

2024-01-02 08:08

本文主要是介绍Android4.1 InputManagerService 流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


1. mInputManager = new InputManagerService(context, mInputMonitor);

   在WMS的构造函数中进行初始化, mInputMonitor 继承InputManagerService.Callbacks,传给InputMangerService中的mCallbacks,WMS中的Context给mContext;

   在InputManagerService的构造函数中,主要是初始化了变量,最后调用了Native的方法

    public InputManagerService(Context context, Callbacks callbacks) {this.mContext = context;this.mCallbacks = callbacks;this.mHandler = new InputManagerHandler();Slog.i(TAG, "Initializing input manager");mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());}


2. nativeInit (frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp)


static jint nativeInit(JNIEnv* env, jclass clazz,jobject serviceObj, jobject contextObj, jobject messageQueueObj) {sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,messageQueue->getLooper());im->incStrong(serviceObj);return reinterpret_cast<jint>(im);
}

   在JNI的nativeInit中,初始化了NativeInputManager,把IMS里面的inputManagerHandle中的Looper传给了NativeInputManager


NativeInputManager::NativeInputManager(jobject contextObj,jobject serviceObj, const sp<Looper>& looper) :mLooper(looper) {... ...sp<EventHub> eventHub = new EventHub();mInputManager = new InputManager(eventHub, this, this);
    在NativeInputManager的构造中,主要就是new出了一个EventHub, 然后作为构造参数给IputManager,最终最为一个参数传给InputReader。 并且和InputManagerHander共用同一个MessageQueue.
   这里面的readerPolicy和dispatcherPolicy实际上就是NativeInputManager对象,后面会用到。

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

void InputManager::initialize() {mReaderThread = new InputReaderThread(mReader);mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

     InputManager的构造函数中主要创建了InputReader 和 InputDispatcher对象并且保存在了mReader和mDispatcher中,InputDispatcher类是负责把键盘消息分发给当前激活的Activity窗口的,而InputReader类则是通过EventHub类来实现读取键盘事件的,后面我们会进一步分析。创建了这两个对象后,还要调用initialize函数来执行其它的初始化操作。

    initialize()这个函数创建了一个InputReaderThread线程实例和一个InputDispatcherThread线程实例,并且分别保存在成员变量mReaderThread和mDispatcherThread中。这里的InputReader实列mReader就是通过这里的InputReaderThread线程实列mReaderThread来读取键盘事件的,而InputDispatcher实例mDispatcher则是通过这里的InputDispatcherThread线程实例mDisptacherThread来分发键盘消息的。

 

3. mInputManager.start()

    InputManagerService 初始化完成之后,WMS就会去调用mInputManager.start() 来开始真正的检测键盘和touch事件。

   

    public void start() {Slog.i(TAG, "Starting input manager");nativeStart(mPtr);... ...}
     InputManagerServcie.start只是直接去调用Native的start方法。

static void nativeStart(JNIEnv* env, jclass clazz, jint ptr) {NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);status_t result = im->getInputManager()->start();if (result) {jniThrowRuntimeException(env, "Input manager could not be started.");}
}

      nativeStart通过NativeInputManager 来获取 InputManager,之后直接真正干事的地方InputManager.start();

status_t InputManager::start() {status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);return OK;
}

    这个函数主要是启动一个DispatcherThread线程和 ReaderThread线程用来读取和分发touch事件,这里的mDispatcherThread和mReaderThread就是我们之前在InputManager创建的两个线程。 调用run函数就会进入threadLoop函数中去,只要threadLoop返回的是true,函数threadoop就会一直被循环调用。于是这两个线程就起到了循环读取事件和分发事件的作用。

    所以我们直接进入到两个线程的threadLoop去查看一下是如何运行的。


4.  InputReaderThread::threadLoop() 

bool InputReaderThread::threadLoop() {mReader->loopOnce();return true;
}
     InputReaderThread::threadLoop中通过调用mReader->loopOnce()来做每一次循环。 mReader是我们之前传进来的InputReader。

void InputReader::loopOnce() {size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);{ // acquire lockAutoMutex _l(mLock);mReaderIsAliveCondition.broadcast();if (count) {processEventsLocked(mEventBuffer, count);}if (mNextTimeout != LLONG_MAX) {nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);if (now >= mNextTimeout) {mNextTimeout = LLONG_MAX;timeoutExpiredLocked(now);}}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();
}
   在这个函数中主要有两个操作,通过InputEent去getEents,然后processEventsLocked. getEent会去检测是否有Eent发生,如果就把Eents放到mEventBuffer中并且返回Event的个数,如果count是>0的数,说明是有Input发生的,然后交给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;}
}

    当有touch或者键盘事件发生时,rawEvent->type都有一个固定的值,这是一个宏定义,具体可以参考bionic/libc/kernel/common/linux/input.h文件。
  type < EventHubInterface::FIRST_SYNTHETIC_EVENT则是说明此次传上来的event type不是device add,remove或者scan的操作,是一个真的touch event。调用processEventsForDeviceLocked去接着处理事件。

struct RawEvent {nsecs_t when;int32_t deviceId;int32_t type;int32_t code;int32_t value;
};

RawEent的结构体。

InputReader::processEventsForDeviceLocked

void InputReader::processEventsForDeviceLocked(int32_t deviceId,const RawEvent* rawEvents, size_t count) {ssize_t deviceIndex = mDevices.indexOfKey(deviceId);InputDevice* device = mDevices.valueAt(deviceIndex);if (device->isIgnored()) {//ALOGD("Discarding event for ignored deviceId %d.", deviceId);return;}device->process(rawEvents, count);
}
    mDevices是一个Vector类型的集合变量,将deviceId和不同的device作为键值进行保存。通过传进来的deviceId值,就可一找到对应需要处理输入事件的device,之后调用device->process来用指定的device去处理事件。 Input device早在之前就通过 InputReader::addDeviceLocked 把自己加到了mDevices中。    


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 (mDropUntilNextSync) {if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {mDropUntilNextSync = false;} else {}} 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);}}}
}

 这里的mMappers 保存了一系列输入设备事件处理象,例如负责处理键盘事件的KeyboardKeyMapper对象、负责处理轨迹球事件的TrackballInputMapper对象以及负责处理触摸屏事件的TouchInputMapper对象, 它们是在InputReader类的成员函数createDeviceLocked中创建的。

 这边假设只是一个单击屏幕的事件,于是就会用TouchInputMapper::process

void TouchInputMapper::process(const RawEvent* rawEvent) {mCursorButtonAccumulator.process(rawEvent);mCursorScrollAccumulator.process(rawEvent);mTouchButtonAccumulator.process(rawEvent);if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {sync(rawEvent->when);}
}

这个函数中先让三个Accumulator去保存此次rawEent的一系列特性参数, 之后就会走到sync函数中(不知到rawEvent->type和rawEvent->code具体代表什么意思)

void TouchInputMapper::sync(nsecs_t when) {// Sync button state.mCurrentButtonState = mTouchButtonAccumulator.getButtonState()| mCursorButtonAccumulator.getButtonState();// Sync scroll state.mCurrentRawVScroll = mCursorScrollAccumulator.getRelativeVWheel();mCurrentRawHScroll = mCursorScrollAccumulator.getRelativeHWheel();mCursorScrollAccumulator.finishSync();// Sync touch state.bool havePointerIds = true;mCurrentRawPointerData.clear();syncTouch(when, &havePointerIds);if (mDeviceMode == DEVICE_MODE_DISABLED) {// Drop all input if the device is disabled.mCurrentRawPointerData.clear();mCurrentButtonState = 0;} else {// Preprocess pointer data.if (!havePointerIds) {assignPointerIds();}// Handle policy on initial down or hover events.uint32_t policyFlags = 0;bool initialDown = mLastRawPointerData.pointerCount == 0&& mCurrentRawPointerData.pointerCount != 0;bool buttonsPressed = mCurrentButtonState & ~mLastButtonState;if (initialDown || buttonsPressed) {// If this is a touch screen, hide the pointer on an initial down.if (mDeviceMode == DEVICE_MODE_DIRECT) {getContext()->fadePointer();}// Initial downs on external touch devices should wake the device.// We don't do this for internal touch screens to prevent them from waking// up in your pocket.// TODO: Use the input device configuration to control this behavior more finely.if (getDevice()->isExternal()) {policyFlags |= POLICY_FLAG_WAKE_DROPPED;}}// Synthesize key down from raw buttons if needed.synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource,policyFlags, mLastButtonState, mCurrentButtonState);// Consume raw off-screen touches before cooking pointer data.// If touches are consumed, subsequent code will not receive any pointer data.if (consumeRawTouches(when, policyFlags)) {mCurrentRawPointerData.clear();}// Cook pointer data.  This call populates the mCurrentCookedPointerData structure// with cooked pointer data that has the same ids and indices as the raw data.// The following code can use either the raw or cooked data, as needed.cookPointerData();// Dispatch the touches either directly or by translation through a pointer on screen.if (mDeviceMode == DEVICE_MODE_POINTER) {for (BitSet32 idBits(mCurrentRawPointerData.touchingIdBits); !idBits.isEmpty(); ) {uint32_t id = idBits.clearFirstMarkedBit();const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS|| pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {mCurrentStylusIdBits.markBit(id);} else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER|| pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {mCurrentFingerIdBits.markBit(id);} else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) {mCurrentMouseIdBits.markBit(id);}}for (BitSet32 idBits(mCurrentRawPointerData.hoveringIdBits); !idBits.isEmpty(); ) {uint32_t id = idBits.clearFirstMarkedBit();const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS|| pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) {mCurrentStylusIdBits.markBit(id);}}// Stylus takes precedence over all tools, then mouse, then finger.PointerUsage pointerUsage = mPointerUsage;if (!mCurrentStylusIdBits.isEmpty()) {mCurrentMouseIdBits.clear();mCurrentFingerIdBits.clear();pointerUsage = POINTER_USAGE_STYLUS;} else if (!mCurrentMouseIdBits.isEmpty()) {mCurrentFingerIdBits.clear();pointerUsage = POINTER_USAGE_MOUSE;} else if (!mCurrentFingerIdBits.isEmpty() || isPointerDown(mCurrentButtonState)) {pointerUsage = POINTER_USAGE_GESTURES;}dispatchPointerUsage(when, policyFlags, pointerUsage);} else {if (mDeviceMode == DEVICE_MODE_DIRECT&& mConfig.showTouches && mPointerController != NULL) {mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);mPointerController->setButtonState(mCurrentButtonState);mPointerController->setSpots(mCurrentCookedPointerData.pointerCoords,mCurrentCookedPointerData.idToIndex,mCurrentCookedPointerData.touchingIdBits);}            dispatchHoverExit(when, policyFlags);dispatchTouches(when, policyFlags);dispatchHoverEnterAndMove(when, policyFlags);}// Synthesize key up from raw buttons if needed.synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource,policyFlags, mLastButtonState, mCurrentButtonState);}// Copy current touch to last touch in preparation for the next cycle.mLastRawPointerData.copyFrom(mCurrentRawPointerData);mLastCookedPointerData.copyFrom(mCurrentCookedPointerData);mLastButtonState = mCurrentButtonState;mLastFingerIdBits = mCurrentFingerIdBits;mLastStylusIdBits = mCurrentStylusIdBits;mLastMouseIdBits = mCurrentMouseIdBits;// Clear some transient state.mCurrentRawVScroll = 0;mCurrentRawHScroll = 0;
}

     如果这是单击事件,会走到mDeviceMode == DEVICE_MODE_DIRECT这个判断中,这里面已经开始去dispatchEvent了。

    总要是这三句话,我没有跟下去看,dispatchTouches(when, policyFlags);


void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) {BitSet32 currentIdBits = mCurrentCookedPointerData.touchingIdBits;BitSet32 lastIdBits = mLastCookedPointerData.touchingIdBits;int32_t metaState = getContext()->getGlobalMetaState();int32_t buttonState = mCurrentButtonState;if (currentIdBits == lastIdBits) {if (!currentIdBits.isEmpty()) {// No pointer id changes so this is a move event.// The listener takes care of batching moves so we don't have to deal with that here.dispatchMotion(when, policyFlags, mSource,AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState,AMOTION_EVENT_EDGE_FLAG_NONE,mCurrentCookedPointerData.pointerProperties,mCurrentCookedPointerData.pointerCoords,mCurrentCookedPointerData.idToIndex,currentIdBits, -1,mOrientedXPrecision, mOrientedYPrecision, mDownTime);}} else {// There may be pointers going up and pointers going down and pointers moving// all at the same time.BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value);BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value);BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value);BitSet32 dispatchedIdBits(lastIdBits.value);// Update last coordinates of pointers that have moved so that we observe the new// pointer positions at the same time as other pointers that have just gone up.bool moveNeeded = updateMovedPointers(mCurrentCookedPointerData.pointerProperties,mCurrentCookedPointerData.pointerCoords,mCurrentCookedPointerData.idToIndex,mLastCookedPointerData.pointerProperties,mLastCookedPointerData.pointerCoords,mLastCookedPointerData.idToIndex,moveIdBits);if (buttonState != mLastButtonState) {moveNeeded = true;}// Dispatch pointer up events.while (!upIdBits.isEmpty()) {uint32_t upId = upIdBits.clearFirstMarkedBit();dispatchMotion(when, policyFlags, mSource,AMOTION_EVENT_ACTION_POINTER_UP, 0, metaState, buttonState, 0,mLastCookedPointerData.pointerProperties,mLastCookedPointerData.pointerCoords,mLastCookedPointerData.idToIndex,dispatchedIdBits, upId,mOrientedXPrecision, mOrientedYPrecision, mDownTime);dispatchedIdBits.clearBit(upId);}// Dispatch move events if any of the remaining pointers moved from their old locations.// Although applications receive new locations as part of individual pointer up// events, they do not generally handle them except when presented in a move event.if (moveNeeded) {ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value);dispatchMotion(when, policyFlags, mSource,AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState, 0,mCurrentCookedPointerData.pointerProperties,mCurrentCookedPointerData.pointerCoords,mCurrentCookedPointerData.idToIndex,dispatchedIdBits, -1,mOrientedXPrecision, mOrientedYPrecision, mDownTime);}// Dispatch pointer down events using the new pointer locations.while (!downIdBits.isEmpty()) {uint32_t downId = downIdBits.clearFirstMarkedBit();dispatchedIdBits.markBit(downId);if (dispatchedIdBits.count() == 1) {// First pointer is going down.  Set down time.mDownTime = when;}            dispatchMotion(when, policyFlags, mSource,AMOTION_EVENT_ACTION_POINTER_DOWN, 0, metaState, buttonState, 0,mCurrentCookedPointerData.pointerProperties,mCurrentCookedPointerData.pointerCoords,mCurrentCookedPointerData.idToIndex,dispatchedIdBits, downId,mOrientedXPrecision, mOrientedYPrecision, mDownTime);}}

在dispatchTouches中,主要通过dispatchMotion去回调Listener,

void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,const PointerProperties* properties, const PointerCoords* coords,const uint32_t* idToIndex, BitSet32 idBits,int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime) {PointerCoords pointerCoords[MAX_POINTERS];PointerProperties pointerProperties[MAX_POINTERS];uint32_t pointerCount = 0;while (!idBits.isEmpty()) {uint32_t id = idBits.clearFirstMarkedBit();uint32_t index = idToIndex[id];pointerProperties[pointerCount].copyFrom(properties[index]);pointerCoords[pointerCount].copyFrom(coords[index]);if (changedId >= 0 && id == uint32_t(changedId)) {action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;}pointerCount += 1;}ALOG_ASSERT(pointerCount != 0);if (changedId >= 0 && pointerCount == 1) {// Replace initial down and final up action.// We can compare the action without masking off the changed pointer index// because we know the index is 0.if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) {action = AMOTION_EVENT_ACTION_DOWN;} else if (action == AMOTION_EVENT_ACTION_POINTER_UP) {action = AMOTION_EVENT_ACTION_UP;} else {// Can't happen.ALOG_ASSERT(false);}}NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,action, flags, metaState, buttonState, edgeFlags,pointerCount, pointerProperties, pointerCoords, xPrecision, yPrecision, downTime);getListener()->notifyMotion(&args);

getListener()返回的实际上就是InputerDipatcher对象,

inline InputListenerInterface* getListener() { return mContext->getListener(); }
去直接调用ContextImpl::getListener() 
......
InputListenerInterface* InputReader::ContextImpl::getListener() {return mReader->mQueuedListener.get();
}
而mQueuedListener在InputReader的构造中传进来的,这就是InputDispater,我们可以去查看什么时候创建InputReader的,上文也提到过。
......
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);

因此我们接着就去看InputDispatcher::notifyMotion

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILSuint32_t policyFlags = args->policyFlags;policyFlags |= POLICY_FLAG_TRUSTED;
    mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags);bool needWake;{ // acquire lockmLock.lock();if (mInputFilterEnabled) {mLock.unlock();MotionEvent event;event.initialize(args->deviceId, args->source, args->action, args->flags,args->edgeFlags, args->metaState, args->buttonState, 0, 0,args->xPrecision, args->yPrecision,args->downTime, args->eventTime,args->pointerCount, args->pointerProperties, args->pointerCoords);policyFlags |= POLICY_FLAG_FILTERED;if (!mPolicy->filterInputEvent(&event, policyFlags)) {return; // event was consumed by the filter}mLock.lock();}// Just enqueue a new motion event.MotionEntry* newEntry = new MotionEntry(args->eventTime,args->deviceId, args->source, policyFlags,args->action, args->flags, args->metaState, args->buttonState,args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,args->pointerCount, args->pointerProperties, args->pointerCoords);        needWake = enqueueInboundEventLocked(newEntry);mLock.unlock();} // release lockif (needWake) {mLooper->wake();}
}

   在NotifyMotion中首先回去调用mPolicy->interceptMotionBeforeQueueing 通过查询policy判断此次事件是否要传给User端,如果需要则通过policyFlags |= POLICY_FLAG_PASS_TO_USER;就加个Flag。里面的其他 策略暂时不清楚。这里的mPolicy实际上就是NativeInputManager对象,NativeInputManager继承了InputDispatcherPolicyInterface和InputReaderPolicyInterface。

void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
    // Policy:// - Ignore untrusted events and pass them along.// - No special filtering for injected events required at this time.// - Filter normal events based on screen state.// - For normal events brighten (but do not wake) the screen if currently dim.if ((policyFlags & POLICY_FLAG_TRUSTED) && !(policyFlags & POLICY_FLAG_INJECTED)) {if (isScreenOn()) {policyFlags |= POLICY_FLAG_PASS_TO_USER;if (!isScreenBright()) {policyFlags |= POLICY_FLAG_BRIGHT_HERE;}} else {JNIEnv* env = jniEnv();jint wmActions = env->CallIntMethod(mCallbacksObj,gCallbacksClassInfo.interceptMotionBeforeQueueingWhenScreenOff,policyFlags);if (checkAndClearExceptionFromCallback(env,"interceptMotionBeforeQueueingWhenScreenOff")) {wmActions = 0;}policyFlags |= POLICY_FLAG_WOKE_HERE | POLICY_FLAG_BRIGHT_HERE;handleInterceptActions(wmActions, when, /*byref*/ policyFlags);}} else {policyFlags |= POLICY_FLAG_PASS_TO_USER;}
}
之后就会走到mPolicy->filterInputEvent(&event, policyFlags), 看看此次event要不要呗filter掉,我们这边是MotionEvent,return ture说明不会被filter,所以继续走下去就到
enqueueInboundEventLocked中。

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {bool needWake = mInboundQueue.isEmpty();
    mInboundQueue.enqueueAtTail(entry);traceInboundQueueLengthLocked();switch (entry->type) {case EventEntry::TYPE_KEY: {......}case EventEntry::TYPE_MOTION: {MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN&& (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)&& mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY&& mInputTargetWaitApplicationHandle != NULL) {int32_t x = int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));int32_t y = int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
            sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(x, y);if (touchedWindowHandle != NULL&& touchedWindowHandle->inputApplicationHandle!= mInputTargetWaitApplicationHandle) {// User touched a different application than the one we are waiting on.// Flag the event, and start pruning the input queue.
                mNextUnblockedEvent = motionEntry;needWake = true;}}break;}}return needWake;
}

 在这个函数中会先把这个Event加到mInboundQueue中,然后去findTouchedWindowAtLock找到我们在Screen上面点击的是哪个Window,如果找到了对象的handle就把mNextUnblockedEvent = motionEntry;  mNextUnblockedEvent 会在InputDispatcher的循环处理中被作为下一个要处理的Event。

之后返回needWake = true 给notifyMotion,就会触发mLooper->wake(); 通过一连串复杂的管道通信,最后会恢复dispatchOnce继续运行


void InputDispatcher::dispatchOnce() {nsecs_t nextWakeupTime = LONG_LONG_MAX;{ // acquire lockAutoMutex _l(mLock);      dispatchOnceInnerLocked(&nextWakeupTime);if (runCommandsLockedInterruptible()) {nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately}} // 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);
}

dispatchOnce()也是直接调用dispatchOnceInnerLocked去完成具体的事物。


void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {nsecs_t currentTime = now();// 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.switch (mPendingEvent->type) {case EventEntry::TYPE_CONFIGURATION_CHANGED: {.......}case EventEntry::TYPE_DEVICE_RESET: {......}case EventEntry::TYPE_KEY: {.......}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;}if (done) {if (dropReason != DROP_REASON_NOT_DROPPED) {dropInboundEventLocked(mPendingEvent, dropReason);}releasePendingEventLocked();*nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately}
}
这个函数中,先通过mPendingEvent = mInboundQueue.dequeueAtHead();去取得当前InboundQueue的Head event付给mPengdingEvent. 然后根据mPendingEvent->type的类型去判断Event的类型,我们这边是TYPE_MOTION, 之后就调用对应的dispatch函数。


bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {// Clean up if dropping the event.if (*dropReason != DROP_REASON_NOT_DROPPED) {setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);return true;}bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;// Identify targets.Vector<InputTarget> inputTargets;bool conflictingPointerActions = false;int32_t injectionResult;if (isPointerEvent) {
        // Pointer event.  (eg. touchscreen)injectionResult = findTouchedWindowTargetsLocked(currentTime,entry, inputTargets, nextWakeupTime, &conflictingPointerActions);} else {// Non touch event.  (eg. trackball)......}ALOGD("injectionResult = %d !!!!!!!!!!!!!!!!!", injectionResult);if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {return false;}    setInjectionResultLocked(entry, injectionResult);if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {return true;}    addMonitoringTargetsLocked(inputTargets);// Dispatch the motion.if (conflictingPointerActions) {CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,"conflicting pointer actions");synthesizeCancelationEventsForAllConnectionsLocked(options);}dispatchEventLocked(currentTime, entry, inputTargets);return true;
}


int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,const MotionEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,bool* outConflictingPointerActions) {enum InjectionPermission {INJECTION_PERMISSION_UNKNOWN,INJECTION_PERMISSION_GRANTED,INJECTION_PERMISSION_DENIED};nsecs_t startTime = now();.......// Success!  Output targets.injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i);addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,touchedWindow.pointerIds, inputTargets);}// Drop the outside or hover touch windows since we will not care about them// in the next iteration.mTempTouchState.filterNonAsIsTouchWindows();


    addWindowTargetLocked 将找到的找到的Window放到InputTargets中,在后面dispatchEvent的时候会从InputTargets中去查找。

void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) {inputTargets.push();const InputWindowInfo* windowInfo = windowHandle->getInfo();InputTarget& target = inputTargets.editTop();target.inputChannel = windowInfo->inputChannel;target.flags = targetFlags;target.xOffset = - windowInfo->frameLeft;target.yOffset = - windowInfo->frameTop;target.scaleFactor = windowInfo->scaleFactor;target.pointerIds = pointerIds;
}


void InputDispatcher::addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets) {for (size_t i = 0; i < mMonitoringChannels.size(); i++) {inputTargets.push();InputTarget& target = inputTargets.editTop();target.inputChannel = mMonitoringChannels[i];target.flags = InputTarget::FLAG_DISPATCH_AS_IS;target.xOffset = 0;target.yOffset = 0;target.pointerIds.clear();target.scaleFactor = 1.0f;}
}


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

这个函数的实现也比较简单,前面我们已经把当前需要接受键盘事件的Activity窗口添加到mCurrentInputTargets中去了,因此,这里就分别把它们取出来,然后调用prepareDispatchCycleLocked函数把键盘事件分发给它们处理。

        关于InputChannel和Connection之间的关系,我转了老罗博客中的一段话:前面我们在分析应用程序注册键盘消息接收通道的过程时,在Step 18中(InputDispatcher.registerInputChannel),把Server端的InputChannel封装成了一个Connection,然后以这个InputChannel中的Receive Pipe Fd作为键值把这个Connection对象保存在mConnectionsByReceiveFd中。这里,既然我们已经通过mCurrentInputTargets得到了表示当前需要接收键盘事件的Activity窗口的InputTarget对象,而且这个InputTarget对象的inputChannel就表示当初在InputDispatcher中注册的Server端InputChannel,因此,这里就可以把这个Connection对象取出来,最后调用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}
enqueueDispatchEntriesLocked(currentTime, connection,splitMotionEntry, inputTarget);splitMotionEntry->release();return;}}// Not splitting.  Enqueue dispatch entries for the event as is.
    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}


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


在开始处理Touch事件之前,这个函数会检查一下传进来的参数connection中的outboundQueue事件队列是否为空,如果不为空,就要看看当前要处理的事件和outboundQueue队列中的最后一个事件是不是同一个motion事件,如果是的话,并且从上面传进来的resumeWithAppendedMotionSample参数为true,这时候就要以流水线的方式来处理这些motion事件了。

         接下来,就会把当前的键盘事件封装成一个DispatchEntry对象,然后添加到connection对象的outboundQueue队列中去,表示当前键盘事件是一个待处理的键盘事件。    

         当connection中的outboundQueue事件队列不为空,即wasEmpty为false时,说明当前这个Activity窗口正在处键盘事件了,因此,就不需要调用startDispatchCycleLocked来启动Activity窗口来处理这个事件了,因为一旦这个Activity窗口正在处键盘事件,它就会一直处理下去,直到它里的connection对象的outboundQueue为空为止。当connection中的outboundQueue事件队列为空时,就需要调用startDispatchCycleLocked来通知这个Activity窗口来执行键盘事件处理的流程了。

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: {.......}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 (size_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 (size_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, dispatchEntry->resolvedFlags,motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState,xOffset, yOffset,motionEntry->xPrecision, motionEntry->yPrecision,motionEntry->downTime, motionEntry->eventTime,motionEntry->pointerCount, motionEntry->pointerProperties,usingCoords);break;}......}// 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);}
}

转自老罗的博客: 这个函数主要围绕传进来的Connection对象做两件事情,一是从它的outboundQueue队列中取出当前需要处理的键盘事件,然后把这个事件记录在它的内部对象inputPublisher中,二是通过它的内部对象inputPublisher通知它所关联的Activity窗口,现在有键盘事件需要处理了。第一件事情是通过调用它的InputPublisher对象的publishKeyEvent函数来完成的,而第二件事情是通过调用它的InputPublisher对象的sendDispatchSignal来完成的。我们先来看InputPublisher的成员函数publishKeyEvent的实现,然后再回来分析它的另外一个成员函数sendDispatchSignal的实现。

      这里所谓的发送信号通知,其实是通过向其内部一个管道的写端写入一个字符来实现的。前面我们分析应用程序注册键盘消息接收通道的过程时,在Step 21中(NativeInputQueue.registerInputChannel),它把一个InputChannel注册到应用程序主线程中的Looper对象中,然后应用程序的主线程就通过这个Looper对象睡眠等待在这个InputChannel中的前向管道中有新的内容可读了,这里的mSendPipeFd就是对应这个前向管道的写端。现在既然向这个前向管道的写端写入新的内容了,于是,应用程序的主线程就被唤醒了。

   InputPublisher 和 InputConsumer都在InputTransport.cpp中,在connection类中就有一个InputPublisher类型的变量。一个用于发布,一个用于接收。

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {ALOGE("channel '%s' ~ Publisher closed input channel or an error occurred.  ""events=0x%x", getInputChannelName(), events);return 0; // remove the callback}if (!(events & ALOOPER_EVENT_INPUT)) {ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  ""events=0x%x", getInputChannelName(), events);return 1;}JNIEnv* env = AndroidRuntime::getJNIEnv();
    status_t status = consumeEvents(env, false /*consumeBatches*/, -1);mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");return status == OK || status == NO_MEMORY ? 1 : 0;
}



status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,bool consumeBatches, nsecs_t frameTime) {bool skipCallbacks = false;for (;;) {uint32_t seq;InputEvent* inputEvent;
        status_t status = mInputConsumer.consume(&mInputEventFactory,consumeBatches, frameTime, &seq, &inputEvent);if (status) {if (status == WOULD_BLOCK) {if (!skipCallbacks && !mBatchedInputEventPending&& mInputConsumer.hasPendingBatch()) {// There is a pending batch.  Come back later.mBatchedInputEventPending = true;env->CallVoidMethod(mReceiverObjGlobal,gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);}return OK;}return status;}if (!skipCallbacks) {jobject inputEventObj;switch (inputEvent->getType()) {case AINPUT_EVENT_TYPE_KEY:case AINPUT_EVENT_TYPE_MOTION:
#if DEBUG_DISPATCH_CYCLEALOGD("channel '%s' ~ Received motion event.", getInputChannelName());
#endif
                inputEventObj = android_view_MotionEvent_obtainAsCopy(env,static_cast<MotionEvent*>(inputEvent));break;}if (inputEventObj) {
                env->CallVoidMethod(mReceiverObjGlobal,gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);} else {}


最后通过一个JNI转到调用InputEventReceiver.dispatchInputEvent 

 // Called from native code.@SuppressWarnings("unused")private void dispatchInputEvent(int seq, InputEvent event) {mSeqMap.put(event.getSequenceNumber(), seq);onInputEvent(event);}

WindowInputEventReceiver继承自InputEventReceiver,onInputEvent实际上就是在 WindowInputEventReceiver中进行调用, WindowInputEventReceiver在ViewRootImpl中。


    void enqueueInputEvent(InputEvent event,InputEventReceiver receiver, int flags, boolean processImmediately) {QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);// Always enqueue the input event in order, regardless of its time stamp.// We do this because the application or the IME may inject key events// in response to touch events and we want to ensure that the injected keys// are processed in the order they were received and we cannot trust that// the time stamp of injected events are monotonic.QueuedInputEvent last = mFirstPendingInputEvent;if (last == null) {mFirstPendingInputEvent = q;} else {while (last.mNext != null) {last = last.mNext;}last.mNext = q;}        if (processImmediately) {doProcessInputEvents();} else {scheduleProcessInputEvents();}}

如果是要立刻处理这个Eent事件就会调用doProcessInputEvents(), 如果不是的话,就调用scheduleProcessInputEvents(), 把这个event放到主线程的Loop里面,然后由ViewRootHandle来处理。

    void doProcessInputEvents() {while (mCurrentInputEvent == null && mFirstPendingInputEvent != null) {QueuedInputEvent q = mFirstPendingInputEvent;mFirstPendingInputEvent = q.mNext;q.mNext = null;mCurrentInputEvent = q;
            deliverInputEvent(q);}


    private void deliverPointerEvent(QueuedInputEvent q) {Log.d(TAG, "deliverPointerEvent ######################");final MotionEvent event = (MotionEvent)q.mEvent;final boolean isTouchEvent = event.isTouchEvent();if (mInputEventConsistencyVerifier != null) {if (isTouchEvent) {mInputEventConsistencyVerifier.onTouchEvent(event, 0);} else {mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);}}// If there is no view, then the event will not be handled.if (mView == null || !mAdded) {finishInputEvent(q, false);return;}// Translate the pointer event for compatibility, if needed.if (mTranslator != null) {mTranslator.translateEventInScreenToAppWindow(event);}// Enter touch mode on down or scroll.final int action = event.getAction();
        if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {ensureTouchMode(true);}// Offset the scroll position.if (mCurScrollY != 0) {event.offsetLocation(0, mCurScrollY);}if (MEASURE_LATENCY) {lt.sample("A Dispatching PointerEvents", System.nanoTime() - event.getEventTimeNano());}// Remember the touch position for possible drag-initiation.if (isTouchEvent) {mLastTouchPoint.x = event.getRawX();mLastTouchPoint.y = event.getRawY();}        // Dispatch touch to view hierarchy.boolean handled = mView.dispatchPointerEvent(event);if (MEASURE_LATENCY) {lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano());}if (handled) {finishInputEvent(q, true);return;}// Pointer event was unhandled.finishInputEvent(q, false);}

在deliverPointerEvent中会通过mView去做真正的dispatch的工作,

    public final boolean dispatchPointerEvent(MotionEvent event) {if (event.isTouchEvent()) {return dispatchTouchEvent(event);} else {return dispatchGenericMotionEvent(event);}}

 public boolean dispatchTouchEvent(MotionEvent event) {if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onTouchEvent(event, 0);}if (onFilterTouchEventForSecurity(event)) {//noinspection SimplifiableIfStatementListenerInfo li = mListenerInfo;if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {return true;}if (onTouchEvent(event)) {return true;}}if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);}return false;}


当mView.dispatchPointerEvent完成之后会返回一个handled值,代表是否被接受处理了, 然后去调 finishInputEvent (ViewRootImpl.java); 

    private void finishInputEvent(QueuedInputEvent q, boolean handled) {if (q != mCurrentInputEvent) {throw new IllegalStateException("finished input event out of order");}if (q.mReceiver != null) {
            q.mReceiver.finishInputEvent(q.mEvent, handled);} else {q.mEvent.recycleIfNeededAfterDispatch();}recycleQueuedInputEvent(q);mCurrentInputEvent = null;if (mFirstPendingInputEvent != null) {scheduleProcessInputEvents();}}

调到 InputEventReceiver.java ----> finishInputEvent

    public final void finishInputEvent(InputEvent event, boolean handled) {if (event == null) {throw new IllegalArgumentException("event must not be null");}if (mReceiverPtr == 0) {Log.w(TAG, "Attempted to finish an input event but the input event "+ "receiver has already been disposed.");} else {int index = mSeqMap.indexOfKey(event.getSequenceNumber());if (index < 0) {Log.w(TAG, "Attempted to finish an input event that is not in progress.");} else {int seq = mSeqMap.valueAt(index);mSeqMap.removeAt(index);nativeFinishInputEvent(mReceiverPtr, seq, handled);}}event.recycleIfNeededAfterDispatch();}

去调用Navetive的函数nativeFinishInputEvent

status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
#if DEBUG_DISPATCH_CYCLEALOGD("channel '%s' ~ Finished input event.", getInputChannelName());
#endif    status_t status = mInputConsumer.sendFinishedSignal(seq, handled);if (status) {ALOGW("Failed to send finished signal on channel '%s'.  status=%d",getInputChannelName(), status);}return status;
}


status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
#if DEBUG_TRANSPORT_ACTIONSALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",mChannel->getName().string(), seq, handled ? "true" : "false");
#endifif (!seq) {ALOGE("Attempted to send a finished signal with sequence number 0.");return BAD_VALUE;}// Send finished signals for the batch sequence chain first.size_t seqChainCount = mSeqChains.size();if (seqChainCount) {uint32_t currentSeq = seq;uint32_t chainSeqs[seqChainCount];size_t chainIndex = 0;for (size_t i = seqChainCount; i-- > 0; ) {const SeqChain& seqChain = mSeqChains.itemAt(i);if (seqChain.seq == currentSeq) {currentSeq = seqChain.chain;chainSeqs[chainIndex++] = currentSeq;mSeqChains.removeAt(i);}}status_t status = OK;while (!status && chainIndex-- > 0) {status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled);}if (status) {// An error occurred so at least one signal was not sent, reconstruct the chain.do {SeqChain seqChain;seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq;seqChain.chain = chainSeqs[chainIndex];mSeqChains.push(seqChain);} while (chainIndex-- > 0);return status;}}// Send finished signal for the last message in the batch.return sendUnchainedFinishedSignal(seq, handled);
}

最后应该由InputPublisher::receiveFinishedSignal,回去接收这个信号处理。

status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
#if DEBUG_TRANSPORT_ACTIONSALOGD("channel '%s' publisher ~ receiveFinishedSignal",mChannel->getName().string());
#endifInputMessage msg;status_t result = mChannel->receiveMessage(&msg);if (result) {*outSeq = 0;*outHandled = false;return result;}if (msg.header.type != InputMessage::TYPE_FINISHED) {ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer",mChannel->getName().string(), msg.header.type);return UNKNOWN_ERROR;}*outSeq = msg.body.finished.seq;*outHandled = msg.body.finished.handled;return OK;
}


InputDispatcher::handleReceiveCallback 里面有一个循环会去调用receiveFinishedSignal, 并且block在那里,等receiveFinishedSignal有返回值。



        在前面分析应用程序注册键盘消息接收通道过程的Step 21中,我们也说过,当应用程序的主线程因为这个InputChannel中的前向管道的写端唤醒时,InputDispatcher的成员函数handleReceiveCallback就会被回调,因此,接下来,应用程序的主线程就会被唤醒,然后执行InputDispatcher的成员函数handleReceiveCallback。


int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {InputDispatcher* d = static_cast<InputDispatcher*>(data);{ // acquire lockAutoMutex _l(d->mLock);ALOGD(" InputDispatcher::handleReceiveCallback !!!!!!!!!!!!!!!~~~~~~~~");ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);if (connectionIndex < 0) {ALOGE("Received spurious receive callback for unknown input channel.  ""fd=%d, events=0x%x", fd, events);return 0; // remove the callback}bool notify;sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {if (!(events & ALOOPER_EVENT_INPUT)) {ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  ""events=0x%x", connection->getInputChannelName(), events);return 1;}nsecs_t currentTime = now();bool gotOne = false;status_t status;for (;;) {uint32_t seq;bool handled;status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);if (status) {break;}d->finishDispatchCycleLocked(currentTime, connection, seq, handled);gotOne = true;}if (gotOne) {d->runCommandsLockedInterruptible();if (status == WOULD_BLOCK) {return 1;}}notify = status != DEAD_OBJECT || !connection->monitor;if (notify) {ALOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",connection->getInputChannelName(), status);}} else {// Monitor channels are never explicitly unregistered.// We do it automatically when the remote endpoint is closed so don't warn// about them.notify = !connection->monitor;if (notify) {ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred.  ""events=0x%x", connection->getInputChannelName(), events);}}// Unregister the channel.d->unregisterInputChannelLocked(connection->inputChannel, notify);return 0; // remove the callback} // release lock
}


参考文章: 老罗的http://blog.csdn.net/luoshengyang/article/details/6882903

UML 图:

https://skydrive.live.com/redir?resid=6C8CF3BF78BE0A95!317&authkey=!ALzNjtukl5Lw73o

这篇关于Android4.1 InputManagerService 流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/561845

相关文章

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除

火语言RPA流程组件介绍--浏览网页

🚩【组件功能】:浏览器打开指定网址或本地html文件 配置预览 配置说明 网址URL 支持T或# 默认FLOW输入项 输入需要打开的网址URL 超时时间 支持T或# 打开网页超时时间 执行后后等待时间(ms) 支持T或# 当前组件执行完成后继续等待的时间 UserAgent 支持T或# User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器

UMI复现代码运行逻辑全流程(一)——eval_real.py(尚在更新)

一、文件夹功能解析 全文件夹如下 其中,核心文件作用为: diffusion_policy:扩散策略核心文件夹,包含了众多模型及基础库 example:标定及配置文件 scripts/scripts_real:测试脚本文件,区别在于前者倾向于单体运行,后者为整体运行 scripts_slam_pipeline:orb_slam3运行全部文件 umi:核心交互文件夹,作用在于构建真

C++/《C/C++程序编译流程》

程序的基本流程如图:   1.预处理        预处理相当于根据预处理指令组装新的C/C++程序。经过预处理,会产生一个没有宏定义,没有条件编译指令,没有特殊符号的输出文件,这个文件的含义同原本的文件无异,只是内容上有所不同。 读取C/C++源程序,对其中的伪指令(以#开头的指令)进行处理将所有的“#define”删除,并且展开所有的宏定义处理所有的条件编译指令,如:“#if”、“

笔记本电脑的具体选购步骤流程

2.1 笔记本电脑的具体选购步骤流程   关于笔记本电脑的选购,一直是热点话题。不管是新手还是老前辈,选购前,总是要先上网查一查,汇总一些信息或经验。因为选购一台笔记本电脑,从它的配置、外观到做工等很多方面都需要考量,所以挑一台自己喜欢的、适合自己的笔记本电脑也是一件很费脑筋的事情。本节将一些选购笔记本电脑的经验进行了总结,供广大读者选购笔记本电脑时参考。   笔记本电脑选购流程如下

基于微信小程序与嵌入式系统的智能小车开发(详细流程)

一、项目概述 本项目旨在开发一款智能小车,结合微信小程序与嵌入式系统,提供实时图像处理与控制功能。用户可以通过微信小程序远程操控小车,并实时接收摄像头采集的图像。该项目解决了传统遥控小车在图像反馈和控制延迟方面的问题,提升了小车的智能化水平,适用于教育、科研和娱乐等多个领域。 二、系统架构 1. 系统架构设计 本项目的系统架构主要分为以下几个部分: 微信小程序:负责用户界面、控制指令的

MySQL B+树查询数据全流程

MySQL B+树查询数据全流程 一、引言 在 MySQL 数据库中,B+树是一种常用的数据结构,用于高效地存储和查询数据。了解 B+树中查询数据的全过程,对于深入理解 MySQL 的工作原理和优化查询性能至关重要。 二、B+树的结构特点 B+树是一种平衡的多路查找树,具有以下结构特点: 每个节点可以存储多个关键字和对应的指针。非叶子节点的关键字起到索引的作用,用于引导查询到相应的子节点