Android Input输入系统之二:KeyEvent注入事件及事件分发流程

相关参考:
《Android按键Input KeyEvent》
《Android Input输入系统之一:KeyEvent事件监听》
《Android Input输入系统之二:KeyEvent注入事件及事件分发流程》
《Android Input输入系统之三:KeyEvent事件分发和上层应用层对事件的接收》
《Android Input输入系统之四:KeyEvent事件中的InputChannel通信》
《Android Input输入系统之五:按键调节音量加减》

在上一篇文章中,《Android Input输入系统之一:KeyEvent事件监听及事件分发流程》,讲解的是读取设备节点/dev/input/event0,并且将事件上应用层分发的流程。

这篇文章讲解的是,模拟按键消息,通过注入事件的方式响应按键消息,并且向上应用层分发的流程。

注意的是,两者向app层分发按键消息的流程是一样的。

//adb命令
adb shell input keyevent 24

注入按键消息

import android.hardware.input.InputManager;
KeyEvent key = KeyEvent.KEYCODE_VOLUME_DOWN;
InputManager.getInstance().injectInputEvent(key, 0);

framework/base/core/java/android/hardware/input/InputManager.java
framework/base/services/core/java/com/android/server/input/InputManagerService.java
framework/base/core/java/android/hardware/input/IInputManager.aidl

InputManager中的injectInputEvent()调用,通过IInputManager.aidl
调用到InputManagerService中的injectInputEvent()

framework/base/services/core/java/com/android/server/input/InputManagerService.java

    @Override // Binder call
    public boolean injectInputEvent(InputEvent event, int mode) {
        return injectInputEventInternal(event, Display.DEFAULT_DISPLAY, mode);
    }

    private boolean injectInputEventInternal(InputEvent event, int displayId, int mode) {
        if (event == null) {
            throw new IllegalArgumentException("event must not be null");
        }
        if (mode != InputManager.INJECT_INPUT_EVENT_MODE_ASYNC
                && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH
                && mode != InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT) {
            throw new IllegalArgumentException("mode is invalid");
        }

        final int pid = Binder.getCallingPid();
        final int uid = Binder.getCallingUid();
        final long ident = Binder.clearCallingIdentity();
        final int result;
        try {
            result = nativeInjectInputEvent(mPtr, event, displayId, pid, uid, mode,
                    INJECTION_TIMEOUT_MILLIS, WindowManagerPolicy.FLAG_DISABLE_KEY_REPEAT);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        switch (result) {
            case INPUT_EVENT_INJECTION_PERMISSION_DENIED:
                Slog.w(TAG, "Input event injection from pid " + pid + " permission denied.");
                throw new SecurityException(
                        "Injecting to another application requires INJECT_EVENTS permission");
            case INPUT_EVENT_INJECTION_SUCCEEDED:
                return true;
            case INPUT_EVENT_INJECTION_TIMED_OUT:
                Slog.w(TAG, "Input event injection from pid " + pid + " timed out.");
                return false;
            case INPUT_EVENT_INJECTION_FAILED:
            default:
                Slog.w(TAG, "Input event injection from pid " + pid + " failed.");
                return false;
        }
    }

关注:nativeInjectInputEvent()

nativeInjectInputEvent()是一个native层Jni接口,实现在com_android_server_input_InputManagerService.cpp中。

base/services/core/jni/com_android_server_input_InputManagerService.cpp
nativeInjectInputEvent(){
	//省略一部分代码
	//...
    return (jint) im->getInputManager()->getDispatcher()->injectInputEvent(
          & keyEvent, displayId, injectorPid, injectorUid, syncMode, timeoutMillis,
          uint32_t(policyFlags));
	//省略一部分代码
	//...
}

调用的是InputDispatcher::injectInputEvent()
native/services/inputflinger/InputDispatcher.cpp,依旧还是native层。

InputDispatcher.cpp的injectInputEvent()中会去判断key类型。这个函数是比较重要的函数,最终的按键消息就是在这里发向应用层的。

//1.调用了PhoneWindowManager中的interceptKeyBeforeQueueing,
// 代码路径:frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
mPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);

//2.根据Event类型,创建EventEntry
firstInjectedEntry = new KeyEntry(keyEvent->getEventTime(),
        keyEvent->getDeviceId(), keyEvent->getSource(),
        policyFlags, action, flags,
        keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(),
        keyEvent->getRepeatCount(), keyEvent->getDownTime());
lastInjectedEntry = firstInjectedEntry;

//3.调用enqueueInboundEventLocked,并判断是否唤醒InputDispatcherThread线程
bool needWake = false;
for (EventEntry* entry = firstInjectedEntry; entry != NULL; ) {
    EventEntry* nextEntry = entry->next;
    needWake |= enqueueInboundEventLocked(entry);
    entry = nextEntry;
}

//4.调用mLooper->wake(),唤醒InputDispatcherThread线程
if (needWake) {
    mLooper->wake();
}

//5.InputDispatcherThread线程中执行了dispatchOnce(),将事件分发出去
bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}

//6.dispatchOnce()调用了dispatchOnceInnerLocked,最终将事件分发给应用层
void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        AutoMutex _l(mLock);
        mDispatcherIsAliveCondition.broadcast();

        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

        // Run all pending commands if there are any.
        // If any commands were run then force the next poll to wake up immediately.
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
    } // release lock

    // Wait for callback or timeout or wake.  (make sure we round up, not down)
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}

具体dispatchOnceInnerLocked中是怎么将事件分开给应用层的呢?

dispatchOnceInnerLocked(){
	//TYPE_KEY类型
	done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
}

dispatchKeyLocked(){
	//省略一部分代码
	//...
	
	// Dispatch the key.
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

dispatchEventLocked(currentTime, entry, inputTargets){
	//省略一部分代码
	//...
	prepareDispatchCycleLocked();
}

调用了prepareDispatchCycleLocked,其中又调用了enqueueDispatchEntriesLocked,最后调用了startDispatchCycleLocked()。

startDispatchCycleLocked()中,EventEntry::TYPE_KEY类型的事件执行的是:

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

即framework/native/libs/input/InputTransport.cpp中,

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

调用了InputChannel的sendMessage()方法。InputChannel定义在InputTransport的内部。

status_t InputChannel::sendMessage(const InputMessage* msg) {
    size_t msgLength = msg->size();
    ssize_t nWrite;
    do {
        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);

	//省略一部分代码
	//...
}

关注代码:nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);

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

所以这里的InputChannel的sendMessage(),本质上是一个socket通信。

流程图:
Android Input输入系统之二:KeyEvent注入事件及事件分发流程_第1张图片
图片来源于:

参考资料:
https://www.jianshu.com/p/f05d6b05ba17

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