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.cppnativeInjectInputEvent(){//省略一部分代码//... 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.javamPolicy->interceptKeyBeforeQueueing(keyEvent, /*byref*/ policyFlags);//2.根据Event类型,创建EventEntryfirstInjectedEntry = 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通信。
流程图:
图片来源于:
参考资料:
https://www.jianshu.com/p/f05d6b05ba17
更多相关文章
- Android(安卓)Wifi模块分析(三)
- Android中dispatchDraw分析
- Android四大基本组件介绍与生命周期
- Android(安卓)Service AIDL
- Android调用天气预报的WebService简单例子
- android打电话发短信
- android 拨打紧急号码,通话时开启免提功能实现
- Android调用.NET Webservice报org.ksoap2.serialization.SoapPri
- android 自定义view