本篇爲雞生蛋系列第二篇文章java
主要講一下inputmanager相關的,即驅動把數據上報到用戶空間後,用戶空間到應用這麼個流程,
在上一遍講內核的input子系統時候,咱們採起的反向分析,即由驅動出發,最後到input core,input子系統架構這麼個由點到面的分析方法,
那分析inputmanager是否可採用這種方法如何呢?實際上,對於Android上層(Native framework/framework, c++/java)的分析,我通常
採用的是由上而下的分析,即從其初始化(main,構造,onFirstRef())開始, 一般在其初始化時候,會重一些很重要的上下層的鏈接,若是由下往上看,會麻煩點,
而後再結合實例,看看他的數據流向是如何的,或者一些重要的API, 例如對於Audio來講,能夠結合播放音樂流程來分析整個系統架構。android
簡單說來,input到應用的流程爲
EventHub監控並讀取/dev/input下數據 --> 給InputReader 加工處理 --> 到InputDispacher --> 找到focused窗口並經過input channel發出去c++
參考文檔:
十分鐘瞭解Android觸摸事件原理(InputManagerService)
https://www.jianshu.com/p/f05...
android控件系統:輸入事件在控件樹中的傳遞
https://blog.csdn.net/renshug...
https://blog.csdn.net/renshug...
InputManagerService分析一:IMS的啓動與事件傳遞
https://blog.csdn.net/lilian0...segmentfault
相關代碼目錄:
Android 9.0 http://androidxref.com/9.0.0_r3/
frameworks/base/services/java/com/android/server/SystemServer.java
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
frameworks/native/services/inputflinger/架構
frameworks/base/services/java/com/android/server/SystemServer.java startOtherServices() { inputManager = new InputManagerService(context); .... wm = WindowManagerService.main(context, inputManager, ServiceManager.addService(Context.INPUT_SERVICE, inputManager, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL); .... inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); inputManager.start(); ...... }
IMS(InputManagerService)的初始化,是從SystemServer開始的,經過搜索代碼(如上),咱們能夠看到構造了一個實例,
並作爲參數傳給了WMS, 由此咱們也猜測,會和WMS有緊密的關係,而後
IMS設置了setWindowManagerCallbacks()並經過start()函數啓動了,
SystemServer裏有關IMS的就這麼幾個地方,咱們再看下構造和start()具體的流程,與WMS的關聯不分析。app
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java // Pointer to native input manager service object. private final long mPtr; public InputManagerService(Context context) { this.mContext = context; this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper()); // config_useDevInputEventForAudioJack配置爲true, 耳機事件可經過input上報 mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); ...... mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue()); ...... LocalServices.addService(InputManagerInternal.class, new LocalService()); } public void start() { Slog.i(TAG, "Starting input manager"); nativeStart(mPtr); .... }
InputManagerService構造和start()主要也是調到JNI的 nativeInit() nativeStart().socket
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) { .... NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, messageQueue->getLooper()); im->incStrong(0); return reinterpret_cast<jlong>(im); } static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) { ...... status_t result = im->getInputManager()->start(); ...... }
nativeInit()又構造了一個 NativeInputManager(),該類可認爲是上層JAVA和下層EventHub InputManager的橋樑,
nativeStart()經過 NativeInputManager最終調到 InputManager 的 start()方法ide
NativeInputManager::NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper) : mLooper(looper), mInteractive(true) { ...... sp<EventHub> eventHub = new EventHub(); mInputManager = new InputManager(eventHub, this, this); }
NativeInputManager()的構造又new了 EventHub 和 InputManager , 其中
eventHub作爲參數傳給了 InputManager()函數
frameworks/native/services/inputflinger/EventHub.cpp EventHub::EventHub(void) : ......{ acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); mEpollFd = epoll_create(EPOLL_SIZE_HINT); // epoll機制 LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); mINotifyFd = inotify_init(); // inotify機制 int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); // 利用inotify監控 DEVICE_PATH(/dev/input)建立和刪除 ...... eventItem.events = EPOLLIN; eventItem.data.u32 = EPOLL_ID_INOTIFY; result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); // 將inotify的fd添加到Epoll監控中 LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno); int wakeFds[2]; result = pipe(wakeFds); //讀寫pipe, InputReader有事件時喚醒 LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); mWakeReadPipeFd = wakeFds[0]; mWakeWritePipeFd = wakeFds[1]; ...... result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); ...... }
EventHub至關於一個集線器,把底層的USB, TOUCH,鼠標等事件統一收集上來,再給上層。
其構造函數當中利用inotify機制監控"/dev/input" 目錄下設備的建立和刪除,這樣當有設備變動時就能夠收到通知了,
構造函數也建立了所須要的mEpollFd,這個做爲IO多路複用的機制,不清楚的能夠查下如何使用,
構造裏將mINotifyFd添加到了epoll裏,在後續input設備建立的時候,也會把input設備的fd添加進去,這樣當有數據或者設備變化時,
EventHub就可獲取這些事件,進一步處理。
構造還建立了兩個pipe,做爲wakeup的讀端和寫端,當InputReader.cpp有事件(配置變動,monitor, 超時請求等)喚醒EventHub處理。oop
InputManager::InputManager( const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { mDispatcher = new InputDispatcher(dispatcherPolicy); mReader = new InputReader(eventHub, readerPolicy, mDispatcher); // eventHub又傳給了 InputReader,最終他們倆是緊密聯繫在一塊兒的 initialize(); // eventHub又傳給了 } void InputManager::initialize() { mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); }
InputManager(),建立了InputDispatcher和InputReader實例並與對應的InputDispatcherThread InputReaderThread 線程關聯
具體的咱們不往下跟了,有興趣的能夠再看看,
至此,初始化流程告一段落。
InputManagerService.java的 start方法,最終到InputManager::start(),
status_t InputManager::start() { status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY); ...... result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); ...... }
start() 方法目的就是讓這兩個線程跑起來,這樣就能夠不斷的獲取,處理消息了。
startOtherServices()/SystemServer.java + new InputManagerService(context) --> nativeInit(...) --> new NativeInputManager(...) + + new EventHub() --> inotify監控/dev/input + epoll + wake pipe + + new InputManager(eventHub,...) + + new InputDispatcher() + + new InputReader(eventHub,...) + + initialize() + + new InputReaderThread(mReader) + + new InputDispatcherThread(mDispatcher) + + ^ + + + inputManager.start() --> nativeStart(mPtr) --> im->getInputManager()->start() --> mDispatcherThread->run() mReaderThread->run()
bool InputReaderThread::threadLoop() { mReader->loopOnce(); return true; } bool InputDispatcherThread::threadLoop() { mDispatcher->dispatchOnce(); return true; }
上一小節講到IMS經過start()函數,最終讓InputReaderThread InputDispatcherThread兩個線程跑起來了,
線程跑起來後,他們由於返回值爲true, 因此他們會不斷的loop, 即不斷的讀取,分發,讀取,分發……
看上面幾行代碼,以爲整個過程很簡單清晰,然而當咱們繼續跟下去看細節的時候,你能 哇~~哇~~哇~~
這一節咱們看看 mReader->loopOnce(), 下一節繼續看Dispatcher過程
void InputReader::loopOnce() { ...... size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); ...... if (count) { processEventsLocked(mEventBuffer, count); } ......// 通知dispather分發 mQueuedListener->flush(); }
InputReader的loopOnce()經過
EventHub getEvents()
得到元數據,而後經過
processEventsLocked()
進一步的處理,
而後再經過
mQueuedListener->flush()
通知InputDispatcher有數據了,該處理了
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { ...... for (;;) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); // Reopen input devices if needed. ...... // Report any devices that had last been added/removed. while (mClosingDevices) { ...... } // 掃描設備 if (mNeedToScanDevices) { mNeedToScanDevices = false; scanDevicesLocked(); mNeedToSendFinishedDeviceScan = true; } ...... // Grab the next input event. bool deviceChanged = false; while (mPendingEventIndex < mPendingEventCount) { const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++]; ...... ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32); ...... Device* device = mDevices.valueAt(deviceIndex); if (eventItem.events & EPOLLIN) { //epoll事件 // 讀取數據 int32_t readSize = read(device->fd, readBuffer, sizeof(struct input_event) * capacity); ...... event->deviceId = deviceId; // <-- 設備id event->type = iev.type; event->code = iev.code; event->value = iev.value; event += 1; ...... // Return now if we have collected any events or if we were explicitly awoken. if (event != buffer || awoken) { break; } // Poll for events. Mind the wake lock dance! ...... int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis); ...... return event - buffer; }
getEvents()會檢查是否須要掃描設備,若是須要的話,則會創建設備KeyedVector向量表,
以後如有數據到來則經過read()函數讀取數據, 返回RawEvent* buffer給processEventsLocked()進行下一步處理,
若啥事都沒有經過epoll_wait()阻塞等待。
原本數據的讀取(read())比較簡單, 這裏只列下設備掃描流程,做爲我的筆記,有興趣的能夠看下
EventHub::scanDevicesLocked() --> scanDirLocked(DEVICE_PATH) "/dev/input" --> while處理 openDeviceLocked() --> status_t EventHub::openDeviceLocked(const char *devicePath) { ...... int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK); ......//一大堆ioctl的信息獲取 if(ioctl(......)) { ......//生成惟一的 deviceId,和device, 作爲mdevices的 key, value. 之後的操做會經常使用到這個deviceId // Allocate device. (The device object takes ownership of the fd at this point.) int32_t deviceId = mNextDeviceId++; Device* device = new Device(fd, deviceId, String8(devicePath), identifier); ...... // Load the configuration file for the device. // 加載這個設備的 idc(Input Device Configuration)配置文件 loadConfigurationLocked(device); ......//能力獲取和分類 // Figure out the kinds of events the device reports. ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask); ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask); ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask); ...... // 設備分類 device->classes |= ......; ...... //加入到epoll當中 if (registerDeviceForEpollLocked(device) != OK) { ...... configureFd(device); ......//加入mDevices並更新 mOpeningDevices 鏈表 addDeviceLocked(device); return OK; }
// 對於咱們的觸屏來講class爲 // See if this is a touch pad. // Is this a new modern multi-touch driver? if (test_bit(ABS_MT_POSITION_X, device->absBitmask) && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) { ...... if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) { device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT; } ......//以後還會加載虛擬key. // Configure virtual keys. if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) { // Load the virtual keys for the touch screen, if any. // We do this now so that we can make sure to load the keymap if necessary. status_t status = loadVirtualKeyMapLocked(device); status_t EventHub::loadVirtualKeyMapLocked(Device* device) { // The virtual key map is supplied by the kernel as a system board property file. ...... path.append("/sys/board_properties/virtualkeys."); path.append(device->identifier.name); ...... return VirtualKeyMap::load(path, &device->virtualKeyMap);
addDeviceLocked()即添加到
EventHub.h KeyedVector<int32_t, Device*> mDevices;
並更新鏈表mOpeningDevices
void EventHub::addDeviceLocked(Device* device) { mDevices.add(device->id, device); device->next = mOpeningDevices; mOpeningDevices = device; }
另外要注意一點的是,在scanDevicesLocked()時候也會建立虛擬鍵盤。
void EventHub::scanDevicesLocked() { status_t res = scanDirLocked(DEVICE_PATH); ....... // 建立虛擬鍵盤 if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) { createVirtualKeyboardLocked(); } }
mEventHub->getEvents(), 反回元數據後,傳給 processEventsLocked()進一步處理
元數據的定義以下,主要記錄了時間,設備id, type, code, value.
struct RawEvent { nsecs_t when; int32_t deviceId; int32_t type; int32_t code; int32_t value; };
其中的deviceId起了個鏈接做用,用於標識eventhub和iputreader中的設備,
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { for (const RawEvent* rawEvent = rawEvents; count;) { int32_t type = rawEvent->type; size_t batchSize = 1; if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) { int32_t deviceId = rawEvent->deviceId; ...... processEventsForDeviceLocked(deviceId, rawEvent, batchSize); ...... case EventHubInterface::DEVICE_ADDED: addDeviceLocked(rawEvent->when, rawEvent->deviceId); case EventHubInterface::DEVICE_REMOVED: ...... case EventHubInterface::FINISHED_DEVICE_SCAN: ...... } }
processEventsLocked()函數有個
processEventsForDeviceLocked() 對於對數據的處理,
另外還根據type, 處理了對設備添加移除,掃描的處理,
你們就有點奇怪了,咦,eventhub掃描設備的時候,不是有處理添加設備嗎?
咋這兒又有添加設備了? 並且看代碼,二者都有個mDevices變量
EventHub.h KeyedVector<int32_t, Device*> mDevices; InputReader.h KeyedVector<int32_t, InputDevice*> mDevices;
上面可看到二者value類型不一樣,他們之間的key 即deviceID是相同的,
其實我我的認爲EventHub中的Device爲設備的自己屬性,是下層設備的實例化,
而InputReader中的InputDevice爲更高層次的抽象,主要用於往上層處理數據,
addDeviceLocked()過程當中還會根據input設備的不一樣屬性設置不一樣的Mapper事件轉換器。
咱們先看下processEventsForDeviceLocked()過程:
void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count) { ssize_t deviceIndex = mDevices.indexOfKey(deviceId); ...... InputDevice* device = mDevices.valueAt(deviceIndex); ...... device->process(rawEvents, count); } void InputDevice::process(const RawEvent* rawEvents, size_t count) { // Process all of the events in order for each mapper. ...... // 可能會有多個mapper size_t numMappers = mMappers.size(); for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) { ...... for (size_t i = 0; i < numMappers; i++) { InputMapper* mapper = mMappers[i]; mapper->process(rawEvent); } ...... }
processEventsForDeviceLocked() --> device->process() --> mapper->process()
最終數據的處理也是經過mapper來處理的,因此咱們還得看下mapper咋添加的
mapper的添加是根據分類來添加的, 以觸屏爲例
frameworks/native/services/inputflinger/InputReader.cpp InputReader::processEventsLocked() --> addDeviceLocked() --> createDeviceLocked() --> // Touchscreens and touchpad devices. if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) { device->addMapper(new MultiTouchInputMapper(device)); } else if (classes & INPUT_DEVICE_CLASS_TOUCH) { device->addMapper(new SingleTouchInputMapper(device)); }
因此觸屏的最後數據處理函數會調到
MultiTouchInputMapper的process函數再也不詳細看
void MultiTouchInputMapper::process(const RawEvent* rawEvent) { TouchInputMapper::process(rawEvent); mMultiTouchMotionAccumulator.process(rawEvent); }
InputReader::loopOnce() 數據處理完後便調用 mQueuedListener->flush() 通知 InputDispatcher 該處理數據了。
void QueuedInputListener::flush() { size_t count = mArgsQueue.size(); for (size_t i = 0; i < count; i++) { NotifyArgs* args = mArgsQueue[i]; args->notify(mInnerListener); delete args; } mArgsQueue.clear(); }
flush()方法即把mArgsQueue Vector一個個取出來,而後再調用notify()方法,
那咱們確定想要知道
1. 數據是咋壓入 mArgsQueue的?
2. notify() 後續流程咋把數據給到 InputDispatcher
1.
void QueuedInputListener::notifyConfigurationChanged( const NotifyConfigurationChangedArgs* args) { mArgsQueue.push(new NotifyConfigurationChangedArgs(*args)); } void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) { mArgsQueue.push(new NotifyKeyArgs(*args)); } void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) { mArgsQueue.push(new NotifyMotionArgs(*args)); } void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) { mArgsQueue.push(new NotifySwitchArgs(*args)); } void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) { mArgsQueue.push(new NotifyDeviceResetArgs(*args)); }
在QueuedInputListener中看到 notifyConfigurationChanged() notifyKey() notifyMotion() notifySwitch() notifyDeviceReset()
當有配置變化或事件時,都會新建立個notify args實例(都繼承自NotifyArgs),而後push到mArgsQueue,
以觸屏事件爲例,push流程爲:
TouchInputMapper::sync() --> processRawTouches() --> cookAndDispatch() --> dispatchTouches() --> dispatchMotion() --> NotifyMotionArgs args(...) getListener()->notifyMotion(&args) --> frameworks/native/services/inputflinger/InputListener.cpp void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) { mArgsQueue.push(new NotifyMotionArgs(*args)); }
以觸屏NotifyMotionArgs爲例,其調用到
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const { listener->notifyMotion(this); }
注意其 listener 爲 InputDispatcher (InputDispatcher 繼承自 InputDispatcherInterface class InputDispatcher : public InputDispatcherInterface),
frameworks/native/services/inputflinger/InputManager.cpp mDispatcher = new InputDispatcher(dispatcherPolicy); mReader = new InputReader(..., ..., mDispatcher); --> InputReader::InputReader(..., ..., ...listener) --> new QueuedInputListener(listener);
因此最終就調到了
InputDispatcher::notifyMotion()
void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { ......// 合法性檢查 if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount, args->pointerProperties)) { ......// 預處理 mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags); ...... if (shouldSendMotionToInputFilterLocked(args)) { mLock.unlock(); MotionEvent event; event.initialize(args->deviceId, args->source, args->action, args->actionButton, 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; // 過濾 if (!mPolicy->filterInputEvent(&event, policyFlags)) { return; // event was consumed by the filter } mLock.lock(); } // Just enqueue a new motion event. MotionEntry* newEntry = new MotionEntry(args->eventTime, args->deviceId, args->source, policyFlags, args->action, args->actionButton, args->flags, args->metaState, args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime, args->displayId, args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0); // 入隊 needWake = enqueueInboundEventLocked(newEntry); mLock.unlock(); } // release lock if (needWake) { // 喚醒 mLooper->wake(); } }
notifyMotion()會先檢查合法性,而後預處理,若是須要過濾則進行過濾處理,
不然構建 MotionEntry,併入隊,隨後將looper喚醒。
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { bool needWake = mInboundQueue.isEmpty(); // 入隊 mInboundQueue.enqueueAtTail(entry); ...... }
InputReader這一側大至就分析完了,數據從InputReader傳到InputDispatcher也清楚了,
接下來看看數據分發。
在開頭也講到,InputDispatcherThread裏不斷的loop,調用dispatchOnce()進行數據的分發。
frameworks/native/services/inputflinger/InputDispatcher.cpp void InputDispatcher::dispatchOnce() { ...... // Run a dispatch loop if there are no pending commands. // The dispatch loop might enqueue commands to run afterwards. if (!haveCommandsLocked()) { // 若是命令列隊爲空, 進行事件分發 dispatchOnceInnerLocked(&nextWakeupTime); } //若是looper裏沒有信息,會阻塞,直到timeoutMillis超時 mLooper->pollOnce(timeoutMillis); }
dispatchOnce()裏若是命令處理完了,纔會調用dispatchOnceInnerLocked()進行事件處理。
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { .....//若是沒有event,抓取一個 // Ready to start a new event. // If we don't already have a pending event, go grab one. if (! mPendingEvent) { if (mInboundQueue.isEmpty()) { ...... } else { // Inbound queue has at least one entry. mPendingEvent = mInboundQueue.dequeueAtHead(); //<---從mInboundQueue隊頭抓個 traceInboundQueueLengthLocked(); } ......//一些錯誤處理,包括anr時間重置,略過 switch (mPendingEvent->type) { case EventEntry::TYPE_CONFIGURATION_CHANGED: .... case EventEntry::TYPE_DEVICE_RESET: .... case EventEntry::TYPE_KEY: ..... ......//對咱們的touch來講是motion事件 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); break; } ...... }
dispatchOnceInnerLocked從mInboundQueue隊列中取出以前的MotionEntry,
而後錯誤處理,對於觸屏事件作dispatchMotionLocked()
bool InputDispatcher::dispatchMotionLocked( nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { ......// 是否爲 point event bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER; // Identify targets. Vector<InputTarget> inputTargets; ...... if (isPointerEvent) { // Pointer event. (eg. touchscreen) injectionResult = findTouchedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime, &conflictingPointerActions); } else { // Non touch event. (eg. trackball) injectionResult = findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); } ...... dispatchEventLocked(currentTime, entry, inputTargets); return true; }
對於point event,會先用
findTouchedWindowTargetsLocked() 找到目標窗口,不然用
findFocusedWindowTargetsLocked() 找到目標窗口
對咱們的觸屏來講,包含有該屬性
frameworks/native/include/android/input.h
AINPUT_SOURCE_TOUCHSCREEN = 0x00001000 | AINPUT_SOURCE_CLASS_POINTER,
找到目標窗口後,再用 dispatchEventLocked() 發給目標窗口
dispatchEventLocked() --> prepareDispatchCycleLocked() --> enqueueDispatchEntriesLocked() void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) { ...... 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); ...... } void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, ...... // Not splitting. Enqueue dispatch entries for the event as is. 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. 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); } }
enqueueDispatchEntryLocked()會根據flag mode進行比較,而後加入到connection的outboundQueue裏
connection->outboundQueue.enqueueAtTail(dispatchEntry);
而後再調用
startDispatchCycleLocked()最終經過socket把事件發出去
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) { ...... case EventEntry::TYPE_MOTION: { ...... // Publish the motion event. status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId, motionEntry->source, motionEntry->displayId, dispatchEntry->resolvedAction, motionEntry->actionButton, dispatchEntry->resolvedFlags, motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState, xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision, motionEntry->downTime, motionEntry->eventTime, motionEntry->pointerCount, motionEntry->pointerProperties, usingCoords); break; } ...... frameworks/native/libs/input/InputTransport.cpp status_t InputPublisher::publishMotionEvent( ...... InputMessage msg; msg.header.type = InputMessage::TYPE_MOTION; ...... // 經過socket發送 return mChannel->sendMessage(&msg); }
數據發送後,又被誰接收到了呢?以後流程又如何呢?
input數據主要有兩種,一個應用,一個MonitoringChannel,
這裏僅簡單的列舉下,詳細的請看看參考文檔
對於應用的接收,須要看input channel是咋創建的,
而後看看findTouchedWindowTargetsLocked(),咋找到目錄窗口,該函數很複雜,
但有個比較重要的是查詢 mWindowHandles, 該變量在setInputWindows()設置,
WindowManagerService.java mInputMonitor.updateInputWindowsLw(false /*force*/); in addWindow() -+ mInputMonitor.updateInputWindowsLw(true /*force*/); in postWindowRemoveCleanupLocked() + mInputMonitor.updateInputWindowsLw(true /*force*/); in relayoutWindow() + mInputMonitor.updateInputWindowsLw(true /*force*/); in relayoutWindow() +--> updateInputWindowsLw() --> mInputMonitor.updateInputWindowsLw(true /*force*/); in removeWindowToken() + mInputMonitor.updateInputWindowsLw(true /*force*/); in startPositioningLocked() + mInputMonitor.updateInputWindowsLw(true /*force*/); in startPositioningLocked() + mInputMonitor.updateInputWindowsLw(true /*force*/); in finishPositioning() -+ InputMonitor.java updateInputWindowsLw() +--> mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag) +--> updateInputWindows() +--> InputManagerService.java setInputWindows() +--> nativeSetInputWindows() +--> im->setInputWindows (NativeInputManager::setInputWindows()) +--> mInputManager->getDispatcher()->setInputWindows() +--> void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) { ...... mWindowHandles = inputWindowHandles;
應用添加窗口設置mWindowHandles如上。在addWindow() relayoutWindow()...過程當中均可能設置該變量
frameworks/base/core/java/android/view/ViewRootImpl.java public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { ...... // addToDisplay() 將調用WMS mService.addWindow() res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mWinFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel); ...... // mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper()); }
對應用來講在setView() 時會調用mWindowSession.addToDisplay(),很後調用addWindow(), 而後win.openInputChannel(outInputChannel)等創建channel操做
addToDisplay() 之會,會將mInputChannel looper,經過 WindowInputEventReceiver綁在一塊兒,
這樣當有數據到來時在looper裏面處理。
WindowInputEventReceiver()流程以下
WindowInputEventReceiver() --> InputEventReceiver.java InputEventReceiver() --> nativeInit() --> frameworks/base/core/jni/android_view_InputEventReceiver.cpp static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject inputChannelObj, jobject messageQueueObj) { ...... sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env, receiverWeak, inputChannel, messageQueue); status_t status = receiver->initialize(); ...... } initialize() (NativeInputEventReceiver::initialize())--> setFdEvents(ALOOPER_EVENT_INPUT) --> // 注意事件類型爲 ALOOPER_EVENT_INPUT void NativeInputEventReceiver::setFdEvents(int events) { if (mFdEvents != events) { mFdEvents = events; int fd = mInputConsumer.getChannel()->getFd(); if (events) { mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL); system/core/libutils/Looper.cpp int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) { ......// 將input channel的fd加入到epoll監控中 int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
** 當socket接收到數據時,經過handle來處理
android_view_InputEventReceiver.cpp int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) { ......// ALOOPER_EVENT_INPUT 事件 if (events & ALOOPER_EVENT_INPUT) { JNIEnv* env = AndroidRuntime::getJNIEnv(); status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL); mMessageQueue->raiseAndClearException(env, "handleReceiveCallback"); return status == OK || status == NO_MEMORY ? 1 : 0; } ...... } handleEvent() --> consumeEvents() --> status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) { ...... status_t status = mInputConsumer.consume(&mInputEventFactory, //取數據 ...... if (inputEventObj) { ... env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj, //調用 dispatchInputEvent() ...... } --> frameworks/base/core/java/android/view/InputEventReceiver.java dispatchInputEvent() --> onInputEvent() --> final class WindowInputEventReceiver extends InputEventReceiver { ...... @Override public void onInputEvent(InputEvent event, int displayId) { enqueueInputEvent(event, this, 0, true); } void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { ...... if (processImmediately) { doProcessInputEvents(); // --> deliverInputEvent(q); } else { scheduleProcessInputEvents(); } } ViewRootImpl.java private void deliverInputEvent(QueuedInputEvent q) { ...... InputStage stage; if (q.shouldSendToSynthesizer()) { stage = mSyntheticInputStage; } else { // mFirstPostImeInputStage = earlyPostImeStage; new EarlyPostImeInputStage(nativePostImeStage); 注意參數爲nativePostImeStage,在 apply(q, onProcess(q)) 返回forward時會用到 stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; } ...... if (stage != null) { handleWindowFocusChanged(); stage.deliver(q); ...... } stage.deliver --> apply(q, onProcess(q)) --> EarlyPostImeInputStage onProcess() --> processPointerEvent() --> (EarlyPostImeInputStage-->NativePostImeInputStage-->ViewPostImeInputStage-->SyntheticInputStage;) ViewPostImeInputStage mView.dispatchPointerEvent(event) View.java dispatchPointerEvent() +-->View.java dispatchTouchEvent() --> li.mOnTouchListener.onTouch(this, event) onTouchEvent(event) + +--> ViewGroup.java dispatchTouchEvent()
在分發input數據時,會把 mMonitoringChannels 加入到目標中,而後經過socket也發給該目標,
InputDispatcher::dispatchMotionLocked() --> addMonitoringTargetsLocked() --> for mMonitoringChannels
在WMS時構造,會經過monitorInput()建立,
以後別的服務可經過WMS registerPointerEventListener() unregisterPointerEventListener() 以listener方式獲取數據
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java private WindowManagerService(......) { ...... if(mInputManager != null) { final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM); mPointerEventDispatcher = inputChannel != null ? new PointerEventDispatcher(inputChannel) : null; @Override public void registerPointerEventListener(PointerEventListener listener) { mPointerEventDispatcher.registerInputEventListener(listener); } @Override public void unregisterPointerEventListener(PointerEventListener listener) { mPointerEventDispatcher.unregisterInputEventListener(listener); }
monitorInput()流程以下:
InputManagerService.java monitorInput() +--> nativeRegisterInputChannel(......, true); +--> NativeInputManager::registerInputChannel() +--> mInputManager->getDispatcher()->registerInputChannel() --> status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle, bool monitor) { ...... if (monitor) { mMonitoringChannels.push(inputChannel); } ...... }
從slot --> RawPointerData --> cookAndDispatch() cookPointerData()進一步處理將值給
mCurrentCookedState.cookedPointerData,主要爲
cookedPointerData.pointerCoords cookedPointerData.pointerProperties
dispatchMotion()時參數傳入cookedPointerData,進一步將數據封裝爲
NotifyMotionArgs
dispatchMotion(when, policyFlags, mSource, ...... mCurrentCookedStat.cookedPointerData.pointerProperties, mCurrentCookedStat.cookedPointerData.pointerCoords, mCurrentCookedStat.cookedPointerData.idToIndex, ......
TouchInputMapper::sync() +-> syncTouch(when, next); --> 數據從slot到outState->rawPointerData.pointers[outCount]; +-> processRawTouches() --> cookAndDispatch() --> dispatchTouches() --> dispatchMotion()
數據處理完後將 NotifyMotionArgs 壓入mArgsQueue
TouchInputMapper::dispatchMotion() --> getListener()->notifyMotion(&args) --> frameworks/native/services/inputflinger/InputListener.cpp void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const { listener->notifyMotion(this); } void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) { mArgsQueue.push(new NotifyMotionArgs(*args)); }
數據從MultiTouchMotionAccumulator::Slot 轉到 RawPointerData::Pointer
void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { size_t inCount = mMultiTouchMotionAccumulator.getSlotCount(); ...... for (size_t inIndex = 0; inIndex < inCount; inIndex++) { const MultiTouchMotionAccumulator::Slot* inSlot = mMultiTouchMotionAccumulator.getSlot(inIndex); ...... RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount]; outPointer.x = inSlot->getX(); outPointer.y = inSlot->getY(); ...... }
加工數據
void TouchInputMapper::processRawTouches(bool timeout) { ....//在處理mRawStatesPending數據時,一個一個取出給mCurrentRawState,而後 cookAndDispatch進一步處理 for(count = 0; count < N; count++) { const RawState& next = mRawStatesPending[count]; ......//給mCurrentRawState mCurrentRawState.copyFrom(next); ......//cookAndDispatch加工並分發 cookAndDispatch(mCurrentRawState.when); void TouchInputMapper::cookPointerData() { uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount; mCurrentCookedState.cookedPointerData.clear(); ......//將數據進一步的處理,例如,計算旋轉後的值 // Walk through the the active pointers and map device coordinates onto // surface coordinates and adjust for display orientation. for (uint32_t i = 0; i < currentPointerCount; i++) { const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i]; ...... case DISPLAY_ORIENTATION_90: x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate; ......//將值給 cookedPointerData.pointerCoords // Write output coords. PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i]; out.clear(); out.setAxisValue(AMOTION_EVENT_AXIS_X, x); out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); ......//將值給 cookedPointerData.pointerProperties // Write output properties. PointerProperties& properties = mCurrentCookedState.cookedPointerData.pointerProperties[i]; uint32_t id = in.id; properties.clear(); properties.id = id; properties.toolType = in.toolType; // Write id index. mCurrentCookedState.cookedPointerData.idToIndex[id] = i; ......