Android输入系统IMS(4)--ims分发过程

一.线程mDispatcherThread分发:

      上一篇讲到了InputDispatcher::notifyKey()。看看这个函数的主要的函数调用:

Android输入系统IMS(4)--ims分发过程_第1张图片

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    KeyEvent event;
    event.initialize(args->deviceId, args->source, args->action,
            flags, keyCode, args->scanCode, metaState, 0,
            args->downTime, args->eventTime);

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

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

        if (shouldSendKeyToInputFilterLocked(args)) {
            mLock.unlock();

            policyFlags |= POLICY_FLAG_FILTERED;
            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                return; // event was consumed by the filter
            }

            mLock.lock();
        }

        int32_t repeatCount = 0;
        KeyEntry* newEntry = new KeyEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, flags, keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);

        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock

    if (needWake) {
        mLooper->wake();
    }
}

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.isEmpty();
    mInboundQueue.enqueueAtTail(entry);
    traceInboundQueueLengthLocked();
    return needWake;
}

     这里的mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);用于对按键的拦截最终会调用到 PhoneWindowManager.java中interceptKeyBeforeQueueing函数对按键就行拦截,类似的拦截函数还有 interceptKeyBeforeDispatching。后面会分析interceptKeyBeforeDispatching函数的调用过程以及应用场合。从图中可以看到输入事件被封装到newEntry变量中并且放入mInboundQueue的队列尾巴中,然后调用mLooper->wake()来唤醒InputDispatccherThread线程了。InputDispatccherThread平时是睡眠等待是否有数据分发。

    唤醒后就该看InputDispatcherThread::threadLoop()函数了。

Android输入系统IMS(4)--ims分发过程_第2张图片

dispatchOnceInnerLocked{
	mPendingEvent = mInboundQueue.dequeueAtHead(); //从mInboundQueue取出输入事件传递给mPendingEvent 
	switch (mPendingEvent->type) {
	case EventEntry::TYPE_KEY:
	KeyEntry* typedEntry = static_cast(mPendingEvent);
		 done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
	case EventEntry::TYPE_MOTION:
		dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime)
}

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {

        CommandEntry* commandEntry = postCommandLocked(
                    & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);

    // Identify targets.
    Vector 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);

    // Dispatch the key.
    dispatchEventLocked(currentTime, entry, inputTargets);//entry 包含输入事件信息
    return true;
}

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector& inputTargets) {
        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {
            sp connection = mConnectionsByFd.valueAt(connectionIndex);
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
        }
}

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
        const sp& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {

    // Not splitting.  Enqueue dispatch entries for the event as is.
    enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget);
}

void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
        const sp& 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);
    }
}

void InputDispatcher::enqueueDispatchEntryLocked(
        const sp& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
        int32_t 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);

    // Enqueue the dispatch entry.
    connection->outboundQueue.enqueueAtTail(dispatchEntry);//dispatchEntry包含有输入事件信息放入connection->outboundQueue中
    traceOutboundQueueLengthLocked(connection);
}

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

 DispatchEntry* dispatchEntry = connection->outboundQueue.head;
 case EventEntry::TYPE_KEY: {
            KeyEntry* keyEntry = static_cast(eventEntry);

            // Publish the key event.
            status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
                    keyEntry->deviceId, keyEntry->source,
                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
                    keyEntry->keyCode, keyEntry->scanCode,
                    keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
                    keyEntry->eventTime);
            break;
}

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

status_t InputChannel::sendMessage(const InputMessage* msg) {
    size_t msgLength = msg->size();
    ssize_t nWrite;
    do {
//最终通过socketpair发送出去,根据之前带代码可以知道是通过wms发给app了。
        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);

}

     通过上面的图片和函数关键代码可知最终输入事件终通过socketpair发送出去,根据之前带代码可以知道是通过wms发给app了。具体是怎么发的下一篇再进行讲解,主要就是这几个InputChannel,ViewRootImpl,WindowManagerService,ViewRootImpl,WindowState 类。到这里native层的输入事件已经通过socketpair发送出去已经完成了任务了,socketpair是socket通信的一种可以方便的在native层和java层通信。

二.mPolicy->interceptKeyBeforeDispatching函数:

      这个分发前截获按键功能在按键定制方面经常用到特别是在车载中控以及TV系统行业中。下面我们分析一下这个函数的最终调用的地方,这样以后就可以方便的做按键定制了。比如本人做车载时方向盘上的按键,在不同的情况下功能不同。(比如方向盘的mute键,在蓝牙电话来电时是接通,通话中长按是挂断,在导航界面短按是...,在launcher界面是...或者检测到第三方app后某些按键不可用,也就是驱动层linux对应的就只有一个keycode值,在framework层要根据当前的app发送不一样的按键值)

Android输入系统IMS(4)--ims分发过程_第3张图片

从上图可以看出现在已经调用到了nsecs_t NativeInputManager::interceptKeyBeforeDispatching();

Android输入系统IMS(4)--ims分发过程_第4张图片

      从上图可以知道最终调用到了 PhoneWindowManager.java中 public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {} 。以后就可以通过修改这个函数来定制按键功能了。修改后执行mmm framework/base编译把生成的jar包推进机器的system/framework目录重启就可以可生效了。

附上项目中修改的部分代码:

Android输入系统IMS(4)--ims分发过程_第5张图片

最后附上native层事件读取可分发时序总图,下一篇将讲解事件是如何从c++层发到japp层的:

Android输入系统IMS(4)--ims分发过程_第6张图片

你可能感兴趣的:(Android系统,Android输入系统,IMS)