framework 學(xué)習(xí)筆記22. input輸入事件番外5(事件分發(fā)InputDispatcher)

1. InputDispatcher 的簡介

input 輸入事件番外4 中講到事件經(jīng)過獲取、初步處理后最終發(fā)送給 InputtDispatcher 進(jìn)行分發(fā)畦木,那么 InputtDispatcher 是怎么進(jìn)行分發(fā)的呢,首先從 InputtDispatcher 的設(shè)計思路出發(fā)拦耐,然后再進(jìn)行一步步分析;
InputDispatcher 既然是要分發(fā)事件见剩,就要搞清兩個問題杀糯,發(fā)送的是什么?發(fā)送給誰苍苞?也就是下面將要展開分析的兩點:
獲取發(fā)送事件固翰;
獲取目標(biāo)app,并將事件交由其處理羹呵;

獲取發(fā)送事件:
第一步:獲取事件骂际;
第二步:放入隊列前的簡單處理;例如是否處于鎖屏狀態(tài)冈欢、有沒有被 InputFilter 消費掉等歉铝;
第三步:放入隊列 Queue<EventEntry> mInboundQueue;

發(fā)送事件給目標(biāo)app:
第一步:找到目標(biāo) app凑耻;
第二步:放入隊列 Queue<DispatchEntry> outboundQueue太示;
第三步:從 outboundQueue 取出事件,通過 Connection 發(fā)送給目標(biāo) app香浩;

2. 獲取發(fā)送事件:

2.1 事件從 InputReader 到 InputDispatcher:上一節(jié)中講到 getListener()->notifyMotion(&args) 后事件就傳遞到 InputDispatcher 交給分發(fā)線程處理了类缤,而且還提到 getListener() 其實就是在初始化 InputReader 時傳入的參數(shù) mDispatcher,這里就來分析一下:

(1)getListener():

// frameworks\native\services\inputflinger\InputManager.cpp
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();
}


// InputReader 的構(gòu)造函數(shù) frameworks\native\services\inputflinger\InputReader.cpp
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);  // 就在這里弃衍,初始化了 mQueuedListener 

    { // acquire lock
        AutoMutex _l(mLock);

        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    } // release lock
}

InputListenerInterface* InputReader::ContextImpl::getListener() {
    return mReader->mQueuedListener.get();  // 通過 get() 函數(shù)獲得原生指針
}

(2)InputReader 傳遞事件到 InputDispatcher:

// 事件獲取線程中 獲取事件到事件傳遞 循環(huán)調(diào)用的方法:loopOnce()
void InputReader::loopOnce() {

    // ... 
    // 1. 讀取事件
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();

        if (count) {
            // 2. 事件的簡單處理呀非;
            processEventsLocked(mEventBuffer, count);  
        }
        // ... 
    } // release lock

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    
    // 3. 把事件傳給 InputDispatcher 處理(也可以理解為交給分發(fā)線程處理)
    mQueuedListener->flush();  // 這個 mQueuedListener 其實就是 InputDispatcher
}

上面的代碼是上一節(jié)內(nèi)容的代碼,還是以 SingleTouch 為例镜盯,事件簡單處理最終調(diào)用 getListener()->notifyMotion(&args)岸裙,然后再調(diào)用 mQueuedListener->flush();

void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    // push() 方法:STL中常見的方法速缆,向數(shù)據(jù)結(jié)構(gòu)中添加元素
    mArgsQueue.push(new NotifyMotionArgs(*args));  
}
 
void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        // mInnerListener 是 QueuedInputListener 構(gòu)造函數(shù)中傳入的 InputDispatcher
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

// 還是以上一節(jié)中為例 所以這里調(diào)用的時 NotifyMotionArgs 的 notify() 方法降允;
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyMotion(this);  // 終于到 InputDispatcher 中了;
}

2.2 InputDispatcher::notifyMotion():從事件獲取到簡單處理 mPolicy->interceptMotionBeforeQueueing()艺糜,最終通過 needWake = true 喚醒分發(fā)線程剧董;
在 InputReader 中通過 listener->notifyMotion(this) 將事件封裝成 NotifyMotionArgs 傳遞到 InputDispatcher;

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
    //(1)獲取發(fā)送事件:即封裝的 NotifyMotionArgs* args  (上面講到的內(nèi)容)
    // ... 省略一些打印信息的代碼
    // 判斷是否是有效的事件
    if (!validateMotionEvent(args->action, args->pointerCount, args->pointerProperties)) {  
        return;
    }

    uint32_t policyFlags = args->policyFlags;
    policyFlags |= POLICY_FLAG_TRUSTED;
    //(2)放入隊列前的簡單處理  
    // 注釋 /*byref*/ 表示 "引用類型的變量"破停,說明這個方法的處理結(jié)果會保存在 policyFlags 中翅楼;并且最后根據(jù)  
    // 這個 policyFlags 構(gòu)造出 newEntry;
    mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags);

    bool needWake;
    { // acquire lock
        mLock.lock();

        if (shouldSendMotionToInputFilterLocked(args)) {
            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;
            /* IMS.filterInputEvent:可攔截事件真慢,當(dāng)返回值為 false 的事件都直接攔截毅臊,沒有機(jī)會加入mInboundQueue隊列 */
            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                return; // event 被 InputFilter 消費掉,直接返回
            }

            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->displayId,
                args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);

        //(3)放入隊列 Queue<EventEntry> mInboundQueue
        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock

    if (needWake) {
        mLooper->wake();  // 如果需要喚醒 InputDispatcher 線程, 則調(diào)用 Looper 的 wake() 方法
    }
}

(1)獲取發(fā)送事件:notifyMotion() 方法中的參數(shù)黑界,即封裝的 NotifyMotionArgs* args管嬉;
(2)放入隊列前的簡單處理: mPolicy->interceptMotionBeforeQueueing()
要想搞明白進(jìn)行了什么處理,首先得先搞明白這個 mPolicy 是什么朗鸠。

// mPolicy 定義:在 InputDispatcher.h 中
class Connection : public RefBase {
  public:
    sp<InputDispatcherPolicyInterface> mPolicy;
}

// mPolicy 的初始化:InputDispatcher 的構(gòu)造函數(shù)中
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
    mPolicy(policy),/* mPolicy 的初始化蚯撩,傳入的參數(shù) */
    mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
    mNextUnblockedEvent(NULL),
    mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
    mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
    mLooper = new Looper(false);
    mKeyRepeatState.lastKeyEntry = NULL;
    policy->getDispatcherConfiguration(&mConfig);
}

跟蹤代碼,結(jié)果發(fā)現(xiàn)是在 frameworks\base\services\core\jni\com_android_server_input_InputManagerService.cpp 中初始化 InputManager(eventHub, this, this) 中傳入的 this (第三個參數(shù))烛占,那就很容易找到 interceptMotionBeforeQueueing() 這個方法的代碼了:

void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) {
    if (mInteractive) {
        policyFlags |= POLICY_FLAG_INTERACTIVE;
    }
    if ((policyFlags & POLICY_FLAG_TRUSTED) && !(policyFlags & POLICY_FLAG_INJECTED)) {
        if (policyFlags & POLICY_FLAG_INTERACTIVE) {
            policyFlags |= POLICY_FLAG_PASS_TO_USER;
        } else {
            JNIEnv* env = jniEnv();
            // 這是 JNI 回調(diào) Java 中的代碼:interceptMotionBeforeQueueingNonInteractive 的同名函數(shù) */
            // 在這里直接說明這個同名函數(shù) PhoneWindowManger.java 中胎挎,后續(xù)單獨一節(jié)來講 JNI 系統(tǒng)
            jint wmActions = env->CallIntMethod(mServiceObj,
                        gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive,
                        when, policyFlags);
            if (checkAndClearExceptionFromCallback(env,
                    "interceptMotionBeforeQueueingNonInteractive")) {
                wmActions = 0;
            }
            // 根據(jù)回調(diào) Java 中的方法得到的結(jié)果 wmActions 來設(shè)置 policyFlags
            handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
        }
    } else {
        if (mInteractive) {
            policyFlags |= POLICY_FLAG_PASS_TO_USER;
        }
    }
}

PhoneWindowManger.java 中:interceptMotionBeforeQueueingNonInteractive()

   @Override
    public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) {
        if ((policyFlags & FLAG_WAKE) != 0) {
            mPowerManager.wakeUp(whenNanos / 1000000);
            return 0;
        }
        if (shouldDispatchInputWhenNonInteractive()) {  // 有沒有鎖屏之類的
            return ACTION_PASS_TO_USER;  // 發(fā)送給 USER
        }
        return 0;
    }

    private boolean shouldDispatchInputWhenNonInteractive() {
        return keyguardIsShowingTq() && mDisplay != null &&
                mDisplay.getState() != Display.STATE_OFF;  // 屏幕不是熄屏狀態(tài)
    }

(3)將事件放入隊列 Queue<EventEntry> mInboundQueue:enqueueInboundEventLocked()

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.isEmpty();  // 如果隊列為空 , 則需要喚醒
    mInboundQueue.enqueueAtTail(entry);  // 插入到mInboundQueue隊列尾部
    traceInboundQueueLengthLocked();

    switch (entry->type) {

    // 這里會優(yōu)化App切換的事件,如果上一個App還有事件沒處理完忆家,也沒反饋事件處理完畢消息
    // 則清空之前的事件呀癣,切換下一個應(yīng)用
    case EventEntry::TYPE_KEY: {
        KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
        if (isAppSwitchKeyEventLocked(keyEntry)) {
            if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
                mAppSwitchSawKeyDown = true;
            } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
                if (mAppSwitchSawKeyDown) {
                    mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
                    mAppSwitchSawKeyDown = false;
                    needWake = true;
                }
            }
        }
        break;
    }

    // 當(dāng)一個非當(dāng)前激活app的點擊事件發(fā)生,會清空之前的事件
    // 從這個新的點擊事件開始
    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 displayId = motionEntry->displayId;
            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(displayId, x, y);
            if (touchedWindowHandle != NULL
                    && touchedWindowHandle->inputApplicationHandle
                            != mInputTargetWaitApplicationHandle) {

                mNextUnblockedEvent = motionEntry;
                needWake = true;
            }
        }
        break;
    }

    return needWake;
}
/*
這里做了兩種優(yōu)化弦赖,主要是在當(dāng)前App窗口處理事件過慢项栏,同時你又觸發(fā)其他App的事件時,Dispatcher就會
丟棄先前的事件蹬竖,從這個開始喚醒Dispatcher沼沈。這樣做很合情合理,用戶在使用時币厕,會遇到App由于開發(fā)者水
平有限導(dǎo)致處理事件過慢情況列另,這時用戶等的不耐煩,則應(yīng)該讓用戶輕松的切換到其它 App旦装,而不是阻塞在
那页衙。所以,事件無法響應(yīng)只會發(fā)生在App內(nèi)部,而不會影響應(yīng)用的切換店乐,從而提升用戶體驗艰躺。App的質(zhì)量問題
不會影響系統(tǒng)的運轉(zhuǎn)。
*/

在這里 needWake 置為 true 后眨八,結(jié)合上面的內(nèi)容腺兴,喚醒了事件分發(fā)線程,接下來就分析一下這個事件分發(fā)線程廉侧。

3. 發(fā)送事件給目標(biāo)app

InputDispatcherThread 與 前一節(jié)的中的 InputReader 線程一樣页响,直接進(jìn)入它的 threadLoop() 方法:

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();  //調(diào)用了 InputDispatcher 的 dispatchOnce() 
    return true;
}

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        AutoMutex _l(mLock);
        mDispatcherIsAliveCondition.broadcast();


        // 第一次進(jìn)來時 mCommandQueue 為空,能進(jìn)入此分支段誊;
        // 然后在 dispatchOnceInnerLocked() 方法中 return闰蚕;
        // 最終在 mLooper->pollOnce(timeoutMillis) 休眠等待;
        if (!haveCommandsLocked()) {  // 為空則開始處理事件
            // 會創(chuàng)建一個 commandEntry连舍,并 mCommandQueue.enqueueAtTail(commandEntry)
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

        // Run all pending commands if there are any.
        // If any commands were run then force the next poll to wake up immediately.
        // 如果 mCommandQueue 不為空没陡,消費掉隊列中的 commandEntry,直到為空
        if (runCommandsLockedInterruptible()) {  // mCommandQueue.isEmpty() 時返回 false
            nextWakeupTime = LONG_LONG_MIN;  // 如果有命令時立刻喚醒分發(fā)線程烟瞧;
        }
    } // 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);
    // looper進(jìn)入休眠等待诗鸭,wake() 方法喚醒(向fd中寫入數(shù)據(jù)就會喚醒)
    mLooper->pollOnce(timeoutMillis);
}

dispatchOnceInnerLocked() -> pokeUserActivityLocked() -> postCommandLocked(),在此過程中從事件隊列中取出事件参滴,調(diào)用 pokeUserActivityLocked() 最終連接 PowerMangerService 保持屏幕喚醒强岸;

// 傳遞流程 這個分支紀(jì)錄一下,暫時不跟進(jìn)砾赔;

// 向 mCommandQueue 中添加 commandEntry:postCommandLocked() 方法中
(1)CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doPokeUserActivityLockedInterruptible)
(2)InputDispatcher.doPokeUserActivityLockedInterruptible ->
(3)com_android_server_input_InputManagerService.pokeUserActivit ->
(4)com_android_server_power_PowerManagerService.android_server_PowerManagerService_userActivity ->
(5)PowerManagerService.userActivityFromNative ->
(6)PowerManagerService.userActivityInternal ->
(7)PowerManagerService.userActivityNoUpdateLocked{ updatePowerStateLocked(); }
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();
    // 判斷事件分發(fā)是否允許蝌箍,也就是在 IMS 未成功啟動、非交互狀態(tài)下等是不可用的暴心,默認(rèn)值是 false
    if (!mDispatchEnabled) {
        resetKeyRepeatLocked();
    }
    //判斷分發(fā)線程是否被凍結(jié)妓盲,是否可以配發(fā),默認(rèn)值是false
    if (mDispatchFrozen) {
        return;
    }
    // 當(dāng)事件分發(fā)的事件點距離該事件加入 mInboundQueue 的時間超過500ms時专普,則判定 app 切換過期悯衬;
    // isAppSwitchDue 為 true;
    bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
    if (mAppSwitchDueTime < *nextWakeupTime) {
        *nextWakeupTime = mAppSwitchDueTime;
    }

    //mPendingEvent是即將要被配發(fā)的事件檀夹,派發(fā)完成置為null筋粗,此處是判斷是否正在配發(fā)事件
    if (! mPendingEvent) {
        if (mInboundQueue.isEmpty()) {  // 如果Event隊列為空的話
            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 {// 有Event時,取出第一個Event炸渡;
            // 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.
       // 重置此次事件分發(fā)的ANR超時時間娜亿,如果超過5秒,就會產(chǎn)生ANR
        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) {
     // 處理 Configuration Change消息 , 即屏幕旋轉(zhuǎn)等等
    case EventEntry::TYPE_CONFIGURATION_CHANGED: {
        ConfigurationChangedEntry* typedEntry =
                static_cast<ConfigurationChangedEntry*>(mPendingEvent);
        done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
        dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped
        break;
    }
    // 處理設(shè)備重置消息 
    case EventEntry::TYPE_DEVICE_RESET: {
        DeviceResetEntry* typedEntry =
                static_cast<DeviceResetEntry*>(mPendingEvent);
        done = dispatchDeviceResetLocked(currentTime, typedEntry);
        dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped
        break;
    }
    // 處理Key按鍵消息
    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);  // 分發(fā)事件
        break;
    }

    default:
        ALOG_ASSERT(false);
        break;
    }

    if (done) {
        if (dropReason != DROP_REASON_NOT_DROPPED) {
            dropInboundEventLocked(mPendingEvent, dropReason);  // 從配發(fā)隊列里面丟棄事件
        }

        releasePendingEventLocked();
        *nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
    }
}

(關(guān)鍵代碼1)分發(fā)事件:done = dispatchMotionLocked();

bool InputDispatcher::dispatchMotionLocked(
        nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
    //...
    bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;

    // 定義 targets(存儲窗口的集合)  找到目標(biāo)窗口
    Vector<InputTarget> inputTargets;

    bool conflictingPointerActions = false;
    int32_t injectionResult;
    if (isPointerEvent) {  
        // Pointer event.  (eg. touchscreen)
      // 如果是手指事件的話 ,則找到 Touch 窗口:關(guān)鍵代碼1
        injectionResult = findTouchedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
    } else {
        // Non touch event.  (eg. trackball)
        // 如果不是手指觸摸事件 , 比如軌跡球事件的話 , 則找到Focus窗口蚌堵;這個分支不是重點
        injectionResult = findFocusedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime);
    }
    // 如果找到窗口失敗, 返回
    if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
        return false;
    }

    setInjectionResultLocked(entry, injectionResult);
    if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
        return true;
    }

    // TODO: support sending secondary display events to input monitors
    if (isMainDisplay(entry->displayId)) {
        addMonitoringTargetsLocked(inputTargets);
    }

    // Dispatch the motion.
    if (conflictingPointerActions) {
        CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
                "conflicting pointer actions");
        synthesizeCancelationEventsForAllConnectionsLocked(options);
    }
    // 開始向窗口分發(fā)事件:關(guān)鍵代碼2
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

3.1 找到目標(biāo) app (也就是找到目標(biāo)窗口):findTouchedWindowTargetsLocked()买决,也是這個方法限制了不同app在不同窗口層級時沛婴,上面的app不能把觸屏事件分發(fā)給下面的app;先挖一個坑吧督赤,后續(xù)寫一章節(jié)來講這里的目標(biāo)窗口和 app 的綁定嘁灯。

int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
        const MotionEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
        bool* outConflictingPointerActions) {
    // ...
    if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
       //從 MotionEntry 中獲取坐標(biāo)點
        int32_t pointerIndex = getMotionEventActionPointerIndex(action);
        int32_t x = int32_t(entry->pointerCoords[pointerIndex].
                getAxisValue(AMOTION_EVENT_AXIS_X));
        int32_t y = int32_t(entry->pointerCoords[pointerIndex].
                getAxisValue(AMOTION_EVENT_AXIS_Y));        
        sp<InputWindowHandle> newTouchedWindowHandle;
        bool isTouchModal = false;
        size_t numWindows = mWindowHandles.size();//1
        // 遍歷窗口,找到觸摸過的窗口和窗口之外的外部目標(biāo)
        for (size_t i = 0; i < numWindows; i++) {//2
            //獲取InputDispatcher中代表窗口的windowHandle 
            sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
            //得到窗口信息windowInfo 
            const InputWindowInfo* windowInfo = windowHandle->getInfo();
            if (windowInfo->displayId != displayId) {
            //如果displayId不匹配够挂,開始下一次循環(huán)
                continue; 
            }
            //獲取窗口的 flag
            int32_t flags = windowInfo->layoutParamsFlags;
            //如果窗口時可見的
            if (windowInfo->visible) {
               //如果窗口的 flag 不為FLAG_NOT_TOUCHABLE(窗口是 touchable)
                if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {
                   // 如果窗口是 focusable 或者 flag 不為FLAG_NOT_FOCUSABLE旁仿,則說明該窗口是"可觸摸模式"
                    isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
                            | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;//3
                   //如果窗口是可觸摸模式或者坐標(biāo)點落在窗口之上(找到目標(biāo)窗口)
                    if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
                        newTouchedWindowHandle = windowHandle;//4
                        break; // found touched window, exit window loop
                    }
                }
                if (maskedAction == AMOTION_EVENT_ACTION_DOWN
                        && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
                    //將符合條件的窗口放入TempTouchState中藕夫,以便后續(xù)處理孽糖。
                    mTempTouchState.addOrUpdateWindow(
                            windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0));//5
                }
            }
        // ...
        }
    } else{
        // ...
    }
    // ...
    // 把臨時存放窗口的 TempTouchState 加入到全局的 inputTargets 中
    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);
    }
   // ...
}

(關(guān)鍵代碼2)開始向窗口分發(fā)事件:dispatchEventLocked(currentTime, entry, inputTargets)

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {

    ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true

    pokeUserActivityLocked(eventEntry);

    for (size_t i = 0; i < inputTargets.size(); i++) {  // 遍歷 inputTargets
        const InputTarget& inputTarget = inputTargets.itemAt(i);
        
        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {
            // 獲取跨進(jìn)程通訊的連接;
            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
            // 通過拿到的連接進(jìn)行分發(fā)毅贮;
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
        } else {
            // ...
        }
    }
}

調(diào)用流程:
prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget) ->
enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget) ->
startDispatchCycleLocked(currentTime, connection)

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
    // ...
    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.
    // 以下方法會調(diào)用 connection->outboundQueue.enqueueAtTail(dispatchEntry)办悟,
    // 將事件放入隊列 Queue<DispatchEntry> outboundQueue;
    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()) {
        // 從 outboundQueue 取出事件滩褥,通過 Connection 發(fā)送給目標(biāo) app病蛉;        
        startDispatchCycleLocked(currentTime, connection);  
    }
}

3.2 將事件放入隊列 Queue<DispatchEntry> outboundQueue:

void InputDispatcher::enqueueDispatchEntryLocked(
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
        int32_t dispatchMode) {
    int32_t inputTargetFlags = inputTarget->flags;
    if (!(inputTargetFlags & dispatchMode)) {
        return;
    }
    inputTargetFlags = (inputTargetFlags & ~InputTarget::FLAG_DISPATCH_MASK) | dispatchMode;

    // This is a new event.
    // Enqueue a new dispatch entry onto the outbound queue for this connection.
    DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
            inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
            inputTarget->scaleFactor);

    // Apply target flags and update the connection's input state.
    switch (eventEntry->type) {
    case EventEntry::TYPE_KEY: {
        KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
        dispatchEntry->resolvedAction = keyEntry->action;
        dispatchEntry->resolvedFlags = keyEntry->flags;

        if (!connection->inputState.trackKey(keyEntry,
                dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
            delete dispatchEntry;
            return; // skip the inconsistent event
        }
        break;
    }

    case EventEntry::TYPE_MOTION: {
        MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
        if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
        } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
        } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
        } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
        } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
        } else {
            dispatchEntry->resolvedAction = motionEntry->action;
        }
        if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
                && !connection->inputState.isHovering(
                        motionEntry->deviceId, motionEntry->source, motionEntry->displayId)) {
            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
        }

        dispatchEntry->resolvedFlags = motionEntry->flags;
        if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
            dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
        }

        if (!connection->inputState.trackMotion(motionEntry,
                dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
            delete dispatchEntry;
            return; // skip the inconsistent event
        }
        break;
    }
    }

    // Remember that we are waiting for this dispatch to complete.
    if (dispatchEntry->hasForegroundTarget()) {
        incrementPendingForegroundDispatchesLocked(eventEntry);
    }

    // Enqueue the dispatch entry.
    connection->outboundQueue.enqueueAtTail(dispatchEntry);
    traceOutboundQueueLengthLocked(connection);
}

3.3 從 outboundQueue 取出事件,通過 Connection 發(fā)送給目標(biāo) app:

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {

    while (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: {
            // ... key 事件
            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. 
            // 通過連接分發(fā)給遠(yuǎn)程端瑰煎;
            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) {
            // ...
            return;
        }

        // Re-enqueue the event on the wait queue.
        connection->outboundQueue.dequeue(dispatchEntry);
        traceOutboundQueueLengthLocked(connection);
        connection->waitQueue.enqueueAtTail(dispatchEntry);
        traceWaitQueueLengthLocked(connection);
    }
}

從 outboundQueue 中取出需要處理的事件铺然,交給 connection 的 inputPublisher 去分發(fā),將事件加入到 connection 的 waitQueue 中酒甸。到這里魄健,事件就從 InputDispatcher 中分發(fā)出去了。

4. 最后的補充

過調(diào)用 inputPublisher.publishMotionEvent()插勤,將事件從 InputDispatcher 分發(fā)出去沽瘦,那這個方法里到底做了些什么呢?
(1)inputPublisher.publishMotionEvent():封裝 InputMessage农尖,并通過 InputChannel 的 sendMessage() 發(fā)送出去析恋;

status_t InputPublisher::publishMotionEvent( // ... 一系列參數(shù) ) {

    if (!seq) {
        ALOGE("Attempted to publish a motion event with sequence number 0.");
        return BAD_VALUE;
    }

    if (pointerCount > MAX_POINTERS || pointerCount < 1) {
        ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %" PRIu32 ".",
                mChannel->getName().string(), pointerCount);
        return BAD_VALUE;
    }

    InputMessage msg;
    msg.header.type = InputMessage::TYPE_MOTION;
    msg.body.motion.seq = seq;
    msg.body.motion.deviceId = deviceId;
    msg.body.motion.source = source;
    msg.body.motion.action = action;
    // ... msg.body.motion 一系列賦值

    for (uint32_t i = 0; i < pointerCount; i++) {
        msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]);
        msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]);
    }
    return mChannel->sendMessage(&msg);  // 調(diào)用了 InputChannel 的 sendMessage() 方法;
}

(2)sendMessage():通過系統(tǒng)的 send() 函數(shù)向 fd 中寫入上面封裝的 InputMessage

status_t InputChannel::sendMessage(const InputMessage* msg) {
    size_t msgLength = msg->size();
    ssize_t nWrite;
    do {
        // send:是一個系統(tǒng)調(diào)用函數(shù)盛卡,用來發(fā)送消息到一個套接字中
        // end()函數(shù)只能在套接字處于連接狀態(tài)的時候才能使用助隧。(只有這樣才知道接受者是誰)
        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);

    if (nWrite < 0) {
        int error = errno;
        if (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) {
        return DEAD_OBJECT;
    }
    return OK;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市滑沧,隨后出現(xiàn)的幾起案子并村,更是在濱河造成了極大的恐慌,老刑警劉巖嚎货,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件橘霎,死亡現(xiàn)場離奇詭異,居然都是意外死亡殖属,警方通過查閱死者的電腦和手機(jī)姐叁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人外潜,你說我怎么就攤上這事原环。” “怎么了处窥?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵嘱吗,是天一觀的道長。 經(jīng)常有香客問我滔驾,道長谒麦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任哆致,我火速辦了婚禮绕德,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘摊阀。我一直安慰自己耻蛇,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布胞此。 她就那樣靜靜地躺著臣咖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪漱牵。 梳的紋絲不亂的頭發(fā)上夺蛇,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機(jī)與錄音布疙,去河邊找鬼蚊惯。 笑死,一個胖子當(dāng)著我的面吹牛灵临,可吹牛的內(nèi)容都是我干的截型。 我是一名探鬼主播官帘,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼缰冤,長吁一口氣:“原來是場噩夢啊……” “哼脚乡!你這毒婦竟也來了染苛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤眶掌,失蹤者是張志新(化名)和其女友劉穎荒吏,沒想到半個月后驱还,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涛碑,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡精堕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蒲障。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歹篓。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡瘫证,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出庄撮,到底是詐尸還是另有隱情背捌,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布洞斯,位于F島的核電站毡庆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏烙如。R本人自食惡果不足惜么抗,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望厅翔。 院中可真熱鬧乖坠,春花似錦搀突、人聲如沸刀闷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽甸昏。三九已至,卻和暖如春徐许,著一層夾襖步出監(jiān)牢的瞬間施蜜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工雌隅, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留翻默,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓恰起,卻偏偏與公主長得像修械,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子检盼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容