android 按键事件响应流程(三)InputDispatch

2019-12-10  本文已影响0人  Ed_Lannister
5982616-0829faeca0627ce7.png 图片.png

InputReader封装好KeyEntries,通知InputDispatch处理事件,先看看Dispatcher的构建函数,新建了一个looper,来做dispatcher的分发循环,并获取了一下Dispatcher的配置信息,这里的配置信息的default值有key的超时和延迟时间,更多的配置在ViewConfiguration中,这些配置都是在input dispatcher初始化的时候加载进来的。

InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) :
    mPolicy(policy),
    mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX),
    mNextUnblockedEvent(NULL),
    mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false),
    mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE), mIsPredumping(false) {
    mLooper = new Looper(false);
    mKeyRepeatState.lastKeyEntry = NULL;
    policy->getDispatcherConfiguration(&mConfig);
}

InputManager在start InputReaderThread之后,同时也启动了InputDispatchThread,这里looper循环里就只有一个dispatchOnce方法,mDispatch是一个InputDispatcherInterface对象,这个接口使用来异步通知系统input reader上报上来的input event事件。

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

首先定义了一个超长的下次唤醒时间,然后去获取锁,检查命令队列里面有没有还没有执行的命令,如果有命令的话就会执行dispatchOnceInnerLocked,如果命令列表为空的话就会入队命令继续运行;

Queue<CommandEntry> mCommandQueue;
···
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    nsecs_t currentTime = now();
    //如果设备处于非交互状态,Dispatch被disable了,就去重置key的repeat计时器,保证当设备刚唤醒的时候抛弃key repeat事件
    if (!mDispatchEnabled) {
        resetKeyRepeatLocked();
    }
    //如果dispatch被冻结,就不处理超时唤醒或者任何新的事件
    if (mDispatchFrozen) {
        return;
    }
    // 当发生app切换,比如home键或者挂断键按下,就把app切换的时间作为下一次唤醒的时间
    bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;
    if (mAppSwitchDueTime < *nextWakeupTime) {
        *nextWakeupTime = mAppSwitchDueTime;
    }
if (! mPendingEvent) {
        //命令队列为空
        if (mInboundQueue.isEmpty()) {
            if (isAppSwitchDue) {
                //刚发生过一次app切换,但此时入站队列是空,所以等的切换事件就不会来,所以不再等待,并重置flag,
                //这里的逻辑应该是,如果有事件,入站队列肯定不应为空
                resetPendingAppSwitchLocked(false);
                isAppSwitchDue = false;
            }
            //mKeyRepeatState是记录key repeat的结构体
            //如果repeat key的nexttime已经过了,就重新做了一个mPendingEvent去处理
            //如果没有nexttime没有过时,但是wakeup的time在repeat time之后,就得让唤醒时间提前。
            if (mKeyRepeatState.lastKeyEntry) {
                if (currentTime >= mKeyRepeatState.nextRepeatTime) {
                    mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
                } else {
                    if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
                        *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
                    }
                }
            }

            // 如果没有重新做一个mPendingEvent事件退出
            if (!mPendingEvent) {
                return;
            }
        } else {
            // 入站队列至少有一条item,就将事件出队列赋值到当前的处理对象上去
            mPendingEvent = mInboundQueue.dequeueAtHead();
            traceInboundQueueLengthLocked();
        }

        // 对比flag看要不要把这一次生成的mPendingEvent捅给用户进程
        if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            pokeUserActivityLocked(mPendingEvent);
        }

        // 准备分发数据
        resetANRTimeoutsLocked();
    }

mPendingEvent是EventEntry对象

struct EventEntry : Link<EventEntry> {
        enum {
            TYPE_CONFIGURATION_CHANGED,
            TYPE_DEVICE_RESET,
            TYPE_KEY,
            TYPE_MOTION
        };
        mutable int32_t refCount;
        int32_t type;
        nsecs_t eventTime;
        uint32_t policyFlags;
        InjectionState* injectionState;
        bool dispatchInProgress; // initially false, set to true while dispatching
        inline bool isInjected() const { return injectionState != NULL; }
        void release();
        virtual void appendDescription(String8& msg) const = 0;
    protected:
        EventEntry(int32_t type, nsecs_t eventTime, uint32_t policyFlags);
        virtual ~EventEntry();
        void releaseInjectionState();
    };

再看看他是怎么往window manager那里捅的

sp<InputWindowHandle> mFocusedWindowHandle;
//InputWindowHandle是window用来接收InputDispatcher传来的input事件,并且能够使InputDispatcher
//间接的操作到window manager的window state
void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) {
    //这个功能可以被window manager 禁用掉
    if (mFocusedWindowHandle != NULL) {
        const InputWindowInfo* info = mFocusedWindowHandle->getInfo();
        if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) {
            return;
        }
    }

    int32_t eventType = USER_ACTIVITY_EVENT_OTHER;
    switch (eventEntry->type) {
    case EventEntry::TYPE_MOTION: 
    case EventEntry::TYPE_KEY: {
        const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry);
        if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) {
            return;
        }
        eventType = USER_ACTIVITY_EVENT_BUTTON;
        break;
    }
    }

    CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doPokeUserActivityLockedInterruptible);
    commandEntry->eventTime = eventEntry->eventTime;
    commandEntry->userActivityEventType = eventType;
}

postCommandLocked是新建了一个CommandEntry对象,并把对应的command入到队尾。

InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
    CommandEntry* commandEntry = new CommandEntry(command);
    mCommandQueue.enqueueAtTail(commandEntry);
    return commandEntry;
}

CommandEntry 是这样一个东西

struct CommandEntry : Link<CommandEntry> {
        CommandEntry(Command command);
        ~CommandEntry();
        Command command;
        sp<Connection> connection;
        nsecs_t eventTime;
        KeyEntry* keyEntry;
        sp<InputApplicationHandle> inputApplicationHandle;
        sp<InputWindowHandle> inputWindowHandle;
        String8 reason;
        int32_t userActivityEventType;
        uint32_t seq;
        bool handled;
    };

运行存在的所有命令,如果有命令执行就立马唤醒下一个poll;
释放锁之后,就开始等待回调、超时唤醒、唤醒

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

        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
    } // release lock

    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}

准备好一次EventEntry事件之后,就开始针对type针对性的处理各种类型的事件,前面说的是入队事件的流程,即使一个事件需要丢弃,也是会走这样的一个流程。这里就以dispatchKeyLocked来看看做了哪些操作。

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

dispatchKeyLocked的实现,首先是一个处理中的flag判断,如果没有处理,就开始看当前keyevent repeatCount是不是等于0,按键是不是down的状态,策略flag是不是可信的,且没有disable掉重复按键逻辑,如果这一次的keyEvent的按键code是和上一次按键的相同,就认为是一次设备驱动生成的重复按键事件,然后更新当前keyevent的repeat count,为前一次的+1,同时释放前一次按键,由当前按键作为下一次按键事件的lastevent,因为是自己生成的repeat key所以,next repeat time设置成超长的时间间隔;如果本次key event 传过来的是新值,就需要设置新的重复按键计时,并将本次key event更新为last keyevent,refcount+1.repeatCount==1的时候就会设置长按的flag到key event的flag里面,然后修改标记为正在处理。

if (! entry->dispatchInProgress) {
        if (entry->repeatCount == 0
                && entry->action == AKEY_EVENT_ACTION_DOWN
                && (entry->policyFlags & POLICY_FLAG_TRUSTED)
                && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
            if (mKeyRepeatState.lastKeyEntry
                    && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) {
                entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1;
                resetKeyRepeatLocked();
                mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves
            } else {
                resetKeyRepeatLocked();
                mKeyRepeatState.nextRepeatTime = entry->eventTime + mConfig.keyRepeatTimeout;
            }
            mKeyRepeatState.lastKeyEntry = entry;
            entry->refCount += 1;
        } else if (! entry->syntheticRepeat) {
            resetKeyRepeatLocked();
        }
        if (entry->repeatCount == 1) {
            entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS;
        } else {
            entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS;
        }

        entry->dispatchInProgress = true;

dispatch policy可以控制处理延迟,可以将本次事件延迟到下一次解决;后续还有允许policy中断keyevent的逻辑,drop的逻辑。
我们关注最重要的这一部分
1.首先是new了一个inputTargets对象,然后通过findFocusedWindowTargetsLocked,找到当前focus的window,这一步里面会检查权限,以及是否有focus的window,以及这个window是否可以接受新的input了,都成功后,会通过addWindowTargetLocked将mFocusedWindowHandle保存的window信息放入到InputTarget对象当中去并将结果setInjectionResultLocked设置进entry中
2.addMonitoringTargetsLocked()是将input target做一个备份,InputChannel会接受所有input的事件的拷贝。

    Vector<sp<InputChannel> > mMonitoringChannels;

3.做好这些工作后,就准备分发key了dispatchEventLocked

    Vector<InputTarget> inputTargets;
    int32_t 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;
    }

    addMonitoringTargetsLocked(inputTargets);

    dispatchEventLocked(currentTime, entry, inputTargets);

dispatchEventLocked这里也做了一个把eventEntry处理一下,发送到command队列里面等待执行。然后针对每个input target,在dispatcher里面,所有的dispatch的事件都通过input target里面指定的一个inputchannel进行连接;所有注册过后的连接,都会通过input channel的文件描述符进行map。
找到connection之后,将eventEntry,currentTime,还有inputTarget一起塞进prepareDispatchCycleLocked。

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
    pokeUserActivityLocked(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 {
            //drop已经不再注册表里特定channel的事件
        }
    }
}

prepareDispatchCycleLocked这里是转了一道

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
    //如果连接状态不是normal就不处理event
    if (connection->status != Connection::STATUS_NORMAL) {
        return;
    }
    //如果有需求就可以分离出motion事件处理
    if (inputTarget->flags & InputTarget::FLAG_SPLIT) {
        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;
        }
    }

    // 如果没有motion分离的需求,直接将事件入队列
    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}

enqueueDispatchEntriesLocked为实际工作对象,这里暂不清楚为什么要将本次eventEntry通过全模式入队出站队列。


void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
    bool wasEmpty = connection->outboundQueue.isEmpty();

    //根据要求的模式,入队事件。
    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 (wasEmpty && !connection->outboundQueue.isEmpty()) {
        startDispatchCycleLocked(currentTime, connection);
    }
}

如果当前出站队列为空的话,就如如下处理,channel通过socket向远端发送eventEntry

switch (eventEntry->type) {
        case EventEntry::TYPE_KEY: {
            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);

            // 封装keyevent消息
            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;
        }
        connection->outboundQueue.dequeue(dispatchEntry);
        connection->waitQueue.enqueueAtTail(dispatchEntry);
}

//将keyevent事件通过mChannel->sendMessage(&msg);发送到出站队列内
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) {
    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);
}

如果出站队列不为空的话enqueueDispatchEntryLocked就处理了一下connection的input state然后就更新了一下,connection的outboundQueue

connection->outboundQueue.enqueueAtTail(dispatchEntry);

至此,我们看到处理逻辑全部都被发送到command队列中去了。

bool InputDispatcher::runCommandsLockedInterruptible() {
    if (mCommandQueue.isEmpty()) {
        return false;
    }

    do {
        CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();

        Command command = commandEntry->command;
        (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible'

        commandEntry->connection.clear();
        delete commandEntry;
    } while (! mCommandQueue.isEmpty());
    return true;
}

viewRootImpl的setView方法中,新建了InputChannel,mWindowSession.addToDisplay最后会传递到WindowManagerService的addWindow方法,把InputChannel传输到WindowManagerService中,建立传输通道,然后又新建了WindowInputEventReceiver,

                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
···
mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);
···
if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }
···

WMS中的addWindow,注册了inputChannel,设定了channel对,数据流向是有InputPublisher和InputConsumer在组合了InputChannel后决定的

            mPolicy.adjustWindowParamsLw(win.mAttrs);
            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));

            res = mPolicy.prepareAddWindowLw(win, attrs);
            if (res != WindowManagerGlobal.ADD_OKAY) {
                return res;
            }

            if (outInputChannel != null && (attrs.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                 try {
                    String name = win.makeInputChannelName();
                    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
                    win.setInputChannel(inputChannels[0]);
                    inputChannels[1].transferTo(outInputChannel);
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
                } catch (IllegalArgumentException e) {
                    Slog.w(TAG, "handle Input channel erorr", e);
                    return WindowManagerGlobal.ADD_INPUTCHANNEL_NOT_ALLOWED;
                }
            }
上一篇下一篇

猜你喜欢

热点阅读