1. 程式人生 > >Android 4 0 事件輸入 Event Input 系統

Android 4 0 事件輸入 Event Input 系統

               

 1. TouchScreen功能在Android4.0下不工作

       原來在Android2.3.5下能正常工作的TouchScreen功能,移植到Android 4.0就不能正常工作了。憑直覺,Android4.0肯定有鬼。真是不看不知道,一看嚇一跳。在Android 4.0中,Event Input地位提高了,你看看,在Adroid2.3.5中,它在frameworks/base/libs/ui之下,在Android4.0中,它在frameworks/base/services/input之下,看到沒有,它有了自己的地位,就像在Kernel中一樣,有自己門戶了。

      再看看程式碼,變化也太大了,當然TouchScreen不能工作,首先自然會看介面部分程式碼。首先看它是如何開啟裝置的,檢視函式EventHub::openDeviceLocked,看看其程式碼,大部分還是很熟悉的,但仔細一看多了一個下面的東東:

ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);

      由於升級到Android4.0時,Kernel還是2.6.35,並沒有進行升級。既然需要EVIOCGPROP,就就看看evdev.c中的ioctl函式是否支援此功能。一看不支援,再看看Kernel3.0.8<這個Kernel版本與Android4.0是一夥的>,我的乖乖,它已經支援了此功能,詳見evdev.c中函式evdev_do_ioctl,這個寫得2.6.35中的友好多了,分別處理:固定長度命令、單個可變長度命令和多個可變長度命令。

      對於為什麼我的TouchScreen在Android4.0不工作,答案顯而易見,我用的Kernel版本不對,當然移植到Android4.0對應的Kernel(Kernel3.0.8)時,TouchScreen驅動本身也需要修改,因為input_dev變化也比較大,比如增加了propbit欄位,以供處理上面的ioctl時使用。

2. Android 4.0如何管理各種驅動裝置

       正是由於遇到上面的問題,才促使自己對Event Input進行深入瞭解。因為蜻蜓點水不是小弟的性格。

       這個年代幹啥都有什麼經理,小弟之類的。比如去飯店吃飯,吃到小強了,總是會大吼一聲,經理,過來看看,然後談打折或賠償的問題。可見經理是不可缺少的,要不然我們找誰來維權啊!

       前面談到的EventHub,這個一看就是一個做實事的,肯定不是領導,哪它的領導是誰呢? 哪我們就從以下幾方面來分析此問題:

       1)每個功能模組是怎麼產生的?

       2)讀取裝置輸入流程?

       3)事件分發流程?

3. 各個功能模組是怎麼產生的?

      先介紹一下每個模組的工作職責:EventHub, InputReader, InputManager...

3.1 模組功能

3.1.1 EventHub

        它是系統中所有事件的中央處理站。它管理所有系統中可以識別的輸入裝置的輸入事件,此外,當裝置增加或刪除時,EventHub將產生相應的輸入事件給系統。

        EventHub通過getEvents函式,給系統提供一個輸入事件流。它也支援查詢輸入裝置當前的狀態(如哪些鍵當前被按下)。而且EventHub還跟蹤每個輸入調入的能力,比如輸入裝置的類別,輸入裝置支援哪些按鍵。 

3.1.2 InputReader

      InputReader從EventHub中讀取原始事件資料(RawEvent),並由各個InputMapper處理之後輸入對應的input listener.

      InputReader擁有一個InputMapper集合。它做的大部分工作在InputReader執行緒中完成,但是InputReader可以接受任意執行緒的查詢。為了可管理性,InputReader使用一個簡單的Mutex來保護它的狀態。

     InputReader擁有一個EventHub物件,但這個物件不是它建立的,而是在建立InputReader時作為引數傳入的。

3.1.3 InputDispatcher

     InputDispatcher負責把事件分發給輸入目標,其中的一些功能(如識別輸入目標)由獨立的policy物件控制。

3.1.4 InputManager

     InputManager是系統事件處理的核心,它雖然不做具體的事,但管理工作還是要做的,比如接受我們客戶的投訴和索賠要求,或者老闆的出所筒。

     InputManager使用兩個執行緒:

     1)InputReaderThread叫做"InputReader"執行緒,它負責讀取並預處理RawEvent,applies policy並且把訊息送入DispatcherThead管理的佇列中。

     2)InputDispatcherThread叫做"InputDispatcher"執行緒,它在佇列上等待新的輸入事件,並且非同步地把這些事件分發給應用程式。

     InputReaderThread類與InputDispatcherThread類不共享內部狀態,所有的通訊都是單向的,從InputReaderThread到InputDispatcherThread。兩個類可以通過InputDispatchPolicy進行互動。

     InputManager類從不與Java互動,而InputDispatchPolicy負責執行所有與系統的外部互動,包括呼叫DVM業務。

3.2 建立流程

1)在android_server_InputManager_nativeInit中建立NativeInputManager物件,並儲存到gNativeInputManager中;

2)在建立NativeInputManager物件時,它會建立EventHub物件<且建立是其成員mNeedToScanDevices的值為true>,然後把剛建立的EventHub物件作為引數建立InputManager物件;

3)在建立InputManager物件時,建立InputReader物件,然後把它作為引數建立InputReaderThread;建立InputDispatcher物件,然後把它作為引數建立InputDispatcherThread物件;(注:以上兩個執行緒物件都有自己的threadLoop函式,它將在Thread::_threadLoop中被呼叫,這個Thread::_threadLoop是執行緒入口函式,執行緒在Thread::run中被真正地建立

4.1)建立InputReader物件

4.1.1)把EventHub、readerPolicy<實質為NativeInputManager物件>和建立的InputDispatcher物件作為引數建立InputReader物件:mReader = new InputReader(eventHub, readerPolicy, mDispatcher);

4.1.2)在建立InputReader時, 儲存EventHub物件到mEventHub中,並建立QueuedInputListener物件並儲存在mQueuedListener中

4.2)建立InputDispatcher物件

4.2.1)把傳入的引數dispatcherPolicy<實質為NativeInputManager物件>作為引數建立InputDispatcher物件:mDispatcher = new InputDispatcher(dispatcherPolicy);

4.2.1)在建立InputDispatcher時,建立了一個looper物件:mLooper = new Looper(false);

3.3 啟動流程

1)在android_server_InputManager_nativeStart中呼叫InputManager::start,程式碼如下:

result = gNativeInputManager->getInputManager()->start();

2)在InputManager::start中,呼叫mDispatcherThread->run和mReaderThread->run,程式碼如下:

result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);

3)在上面的Thread::run中,呼叫createThreadEtc函式,並以Thread::_threadLoop作為入口函式,以上面的mDispatcherThread或mReaderThread作為userdata建立執行緒

4)至此InputReader執行緒和InputDispatcher執行緒都已經工作,詳細資訊見Thread::_threadLoop,在此函式中它將呼叫mDispatcherThread或mReaderThread的threadLoop函式來做真正的事

5.1)mReaderThread->threadLoop

bool InputReaderThread::threadLoop() {    mReader->loopOnce();    return true;}

5.2)mDispatcherThread->threadLoop

bool InputDispatcherThread::threadLoop() {    mDispatcher->dispatchOnce();    return true;} 

3.4 EventInput物件關係圖 

 

4. 裝置操作流程

從EventHub::getEvents讀取的事件資料結構如下:

struct RawEvent {    nsecs_t when;        //事件發生的時間    int32_t deviceId;    //產生此事件的裝置,比如傳送FINISHED_DEVICE_SCAN,不需要填此項    int32_t type;        //事件型別(如:DEVICE_ADDED,DEVICE_REMOVED,FINISHED_DEVICE_SCAN)    int32_t scanCode;    int32_t keyCode;    int32_t value;    uint32_t flags;};

讀取事件時的呼叫流程為:

Thread::_threadLoop->

     InputReaderThread::threadLoop->

          InputReader::loopOnce->

               EventHub::getEvents->

4.1 開啟裝置

      在EventHub::getEvents中,當mNeedToScanDevices為true時<當建立EventHub物件時,它就為true>,它將從/dev/input目錄下查詢所有裝置,並進行開啟,獲取其相關屬性,最後加入mDevices列表中。

EventHub::scanDevicesLocked->

     EventHub::scanDirLocked("/dev/input")->

         EventHub::openDeviceLocked

4.1.1 開啟事件輸入裝置

     開啟事件輸入裝置,在使用者態呼叫open,則在kernel態中呼叫evdev_open函式,evdev_open處理流程如下:

     1)首先從引數inode中獲取在evdev_table中的索引,從而獲取對應的evdev物件

     2)建立evdev_client物件,建立此物件時同時為其buffer成員分配對應的記憶體

     3)把新建立evdev_client物件新增到client_list連結串列中

     4)把client儲存在file的private_data中

     5)呼叫evdev_open_device->input_open_device->input_dev.open函式開啟裝置。

4.2 讀取輸入事件

      要說EventHub::getEvents如何獲取輸入事件,不得不先說說它的幾個相關的成員變數:

     1)mPendingEventCount:呼叫epoll_wait時的返回值,當然如果沒有事件,則其值為0;

     2)mPendingEventIndex:當前需要處理的事件索引

     3)mEpollFd:epoll例項,在EventHub::EventHub中初始化此例,所有輸入事件通過epoll_wait來獲取,每一個事件的資料結構為:struct epoll_event,為了搞明白如何讀取輸入事件的原理,不得不對epoll相關的東東搞個清清楚楚,明明白白,見epoll kernel實現原理注:epoll_event只表明某個裝置上有事件,並不包含事件內容,具體事件內容需要通過read來讀取

   struct epoll_event定義如下:

typedef union epoll_data {    void *ptr;    int fd;    unsigned int u32;    unsigned long long u64;} epoll_data_t;struct epoll_event {    unsigned int events;    epoll_data_t data;};

     每個裝置被建立(在函式EventHub::openDeviceLocked中)時,都會向epoll註冊,程式碼如下:

    // Register with epoll.    struct epoll_event eventItem;    memset(&eventItem, 0, sizeof(eventItem));    eventItem.events = EPOLLIN;    eventItem.data.u32 = deviceId;    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {        LOGE("Could not add device fd to epoll instance.  errno=%d", errno);        delete device;        return -1;    }

4.2.1 檢視裝置上是否有事件

        在呼叫epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis)之後,讀到的epoll_event事件儲存在mPendingEventItems,總共的事件數儲存在mPendingEventCount,當然,在呼叫epoll_event之前,mPendingEventIndex被清0,直正的事件處理在下面的程式碼中。

        // Grab the next input event.        bool deviceChanged = false;        while (mPendingEventIndex < mPendingEventCount) {            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];            if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {                if (eventItem.events & EPOLLIN) {                    mPendingINotify = true;                } else {                    LOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);                }                continue;            }            if (eventItem.data.u32 == EPOLL_ID_WAKE) {                if (eventItem.events & EPOLLIN) {                    LOGV("awoken after wake()");                    awoken = true;                    char buffer[16];                    ssize_t nRead;                    do {                        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));                    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));                } else {                    LOGW("Received unexpected epoll event 0x%08x for wake read pipe.",                            eventItem.events);                }                continue;            }            ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);            if (deviceIndex < 0) {                LOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",                        eventItem.events, eventItem.data.u32);                continue;            }            Device* device = mDevices.valueAt(deviceIndex);            if (eventItem.events & EPOLLIN) {                int32_t readSize = read(device->fd, readBuffer,                        sizeof(struct input_event) * capacity);                if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {                    // Device was removed before INotify noticed.                    LOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d capacity: %d errno: %d)\n",                         device->fd, readSize, bufferSize, capacity, errno);                    deviceChanged = true;                    closeDeviceLocked(device);                } else if (readSize < 0) {                    if (errno != EAGAIN && errno != EINTR) {                        LOGW("could not get event (errno=%d)", errno);                    }                } else if ((readSize % sizeof(struct input_event)) != 0) {                    LOGE("could not get event (wrong size: %d)", readSize);                } else {                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;                    size_t count = size_t(readSize) / sizeof(struct input_event);                    for (size_t i = 0; i < count; i++) {                        const struct input_event& iev = readBuffer[i];                        LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d",                                device->path.string(),                                (int) iev.time.tv_sec, (int) iev.time.tv_usec,                                iev.type, iev.code, iev.value);#ifdef HAVE_POSIX_CLOCKS                        // Use the time specified in the event instead of the current time                        // so that downstream code can get more accurate estimates of                        // event dispatch latency from the time the event is enqueued onto                        // the evdev client buffer.                        //                        // The event's timestamp fortuitously uses the same monotonic clock                        // time base as the rest of Android.  The kernel event device driver                        // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().                        // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere                        // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a                        // system call that also queries ktime_get_ts().                        event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL                                + nsecs_t(iev.time.tv_usec) * 1000LL;                        LOGV("event time %lld, now %lld", event->when, now);#else                        event->when = now;#endif                        event->deviceId = deviceId;                        event->type = iev.type;                        event->scanCode = iev.code;                        event->value = iev.value;                        event->keyCode = AKEYCODE_UNKNOWN;                        event->flags = 0;                        if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {                            status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,                                        &event->keyCode, &event->flags);                            LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",                                    iev.code, event->keyCode, event->flags, err);                        }                        event += 1;                    }                    capacity -= count;                    if (capacity == 0) {                        // The result buffer is full.  Reset the pending event index                        // so we will try to read the device again on the next iteration.                        mPendingEventIndex -= 1;                        break;                    }                }            } else {                LOGW("Received unexpected epoll event 0x%08x for device %s.",                        eventItem.events, device->identifier.name.string());            }        }

4.2.2 讀取裝置上真正的事件

epoll_wait只是告訴我們Device已經有事件了,讓我們去讀,真正讀取裝置輸入事件的程式碼如上,其流程如下:1)根據eventItem.data.u32獲取裝置索引,從而獲取對應的Device

2)從device->fd中讀取input_event事件。read(device->fd, readBuffer, sizeof(struct input_event) * capacity);這些input_event是由各個註冊的input_device報告給input子系統的。具體讀入流程參見

至此,事件已經讀取到使用者態,哪我們就看看EventHub怎麼處理這些事件了。 

4.3 處理輸入事件

      在4.2中,首先通過epoll_wait檢視哪些裝置有事件,然後通過read從有事件的裝置中讀取事件,現在事件已經讀取到使用者態,且資料結構為input_event,儲存在EventHub::getEvents的readBuffer中。下面就看看這些事件下一步的東家是誰?

      1)首先把input_event的資訊填入RawEvent中,其相關程式碼如下:

#ifdef HAVE_POSIX_CLOCKS                        // Use the time specified in the event instead of the current time                        // so that downstream code can get more accurate estimates of                        // event dispatch latency from the time the event is enqueued onto                        // the evdev client buffer.                        //                        // The event's timestamp fortuitously uses the same monotonic clock                        // time base as the rest of Android.  The kernel event device driver                        // (drivers/input/evdev.c) obtains timestamps using ktime_get_ts().                        // The systemTime(SYSTEM_TIME_MONOTONIC) function we use everywhere                        // calls clock_gettime(CLOCK_MONOTONIC) which is implemented as a                        // system call that also queries ktime_get_ts().                        event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL                                + nsecs_t(iev.time.tv_usec) * 1000LL;                        LOGV("event time %lld, now %lld", event->when, now);#else                        event->when = now;#endif                        event->deviceId = deviceId;                        event->type = iev.type;                        event->scanCode = iev.code;                        event->value = iev.value;                        event->keyCode = AKEYCODE_UNKNOWN;                        event->flags = 0;                        if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {                            status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,                                        &event->keyCode, &event->flags);                            LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",                                    iev.code, event->keyCode, event->flags, err);                        }

     2)如果是input_event的型別為EV_KEY,則需要呼叫device->keyMap.keyLayoutMap->mapKey函式把iput_event.code對映為RawEvent.keyCode。相關資料結構關係如下圖所示:

       至此,EventHub::getEvents讀取事件的任務已經完成,下面看看這些RawEvent的命運如何呢?

 4.3.1 InputReader::loopOnce如何處理RawEvent?

    為此,先溫習一下讀取事件時的呼叫流程為:

Thread::_threadLoop->

     InputReaderThread::threadLoop->

          InputReader::loopOnce->

               EventHub::getEvents->

     在InputReader::loopOnce中,當呼叫EventHub->getEvents獲取到RawEvent之後,呼叫InputReader::processEventsLocked來處理這些事件,然後呼叫mQueuedListener->flush()把這些佇列中的事件傳送到Listener。

4.3.1.1 InputReader::processEventsLocked

       在InputReader::processEventsLocked主要分兩步處理:

       1)處理來自於事件驅動裝置的事件(processEventsForDeviceLocked)

       2)處理裝置增加、刪除和修改事件

       按照程式執行流程,應該是先有裝置,然後才會有裝置事件,所以先分析裝置增加。 其程式碼如下:

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;            while (batchSize < count) {                if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT                        || rawEvent[batchSize].deviceId != deviceId) {                    break;                }                batchSize += 1;            }          //處理來自於同一個事件驅動裝置的1個或多個事件            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);        }         else         {            //處理增加或刪除事件驅動裝置的事件,在EventHub::getEvents中產生,            //不是由事件驅動裝置產生的。            switch (rawEvent->type) {            case EventHubInterface::DEVICE_ADDED:                addDeviceLocked(rawEvent->when, rawEvent->deviceId);                break;            case EventHubInterface::DEVICE_REMOVED:                removeDeviceLocked(rawEvent->when, rawEvent->deviceId);                break;            case EventHubInterface::FINISHED_DEVICE_SCAN:                handleConfigurationChangedLocked(rawEvent->when);                break;            default:                LOG_ASSERT(false); // can't happen                break;            }        }        count -= batchSize;        rawEvent += batchSize;    }}
4.3.1.1.1 裝置增加事件處理 addDeviceLocked

      它處理其中的EventHubInterface::DEVICE_ADDED, EventHubInterface:: DEVICE_REMOVED和EventHubInterface::FINISHED_DEVICE_SCAN事件,即與Device相關的事件,這些事件是在EventHub::getEvents中產生的,並不是Kernel態的事件輸入裝置產生的。

     下面分析它如何處理EventHubInterface::DEVICE_ADDED事件。檢視其它程式碼,它是呼叫InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId)來處理此事件。

      在InputReader::addDeviceLocked中的呼叫流程:

      1)先根據mContext, deviceId, name, classes建立一個InputDevice物件,它用於表示單個輸入裝置的狀態。其中的classes為對應Device的classes成員,它用於表示裝置型別,其定義如下:

/* * Input device classes. */enum {    /* The input device is a keyboard or has buttons. */    INPUT_DEVICE_CLASS_KEYBOARD      = 0x00000001,    /* The input device is an alpha-numeric keyboard (not just a dial pad). */    INPUT_DEVICE_CLASS_ALPHAKEY      = 0x00000002,    /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */    INPUT_DEVICE_CLASS_TOUCH         = 0x00000004,    /* The input device is a cursor device such as a trackball or mouse. */    INPUT_DEVICE_CLASS_CURSOR        = 0x00000008,    /* The input device is a multi-touch touchscreen. */    INPUT_DEVICE_CLASS_TOUCH_MT      = 0x00000010,    /* The input device is a directional pad (implies keyboard, has DPAD keys). */    INPUT_DEVICE_CLASS_DPAD          = 0x00000020,    /* The input device is a gamepad (implies keyboard, has BUTTON keys). */    INPUT_DEVICE_CLASS_GAMEPAD       = 0x00000040,    /* The input device has switches. */    INPUT_DEVICE_CLASS_SWITCH        = 0x00000080,    /* The input device is a joystick (implies gamepad, has joystick absolute axes). */    INPUT_DEVICE_CLASS_JOYSTICK      = 0x00000100,    /* The input device is external (not built-in). */    INPUT_DEVICE_CLASS_EXTERNAL      = 0x80000000,}

      建立InputDevice物件之後, 對於多點觸控裝置(class為INPUT_DEVICE_CLASS_TOUCH_MT),建立MultiTouchInputMapper物件並增加到InputDevice的mMappers向量列表中。

      對於單點觸控裝置(class為INPUT_DEVICE_CLASS_TOUCH),建立SingleTouchInputMapper物件並增加到InputDevice的mMappers向量列表中。相關程式碼如下:

InputDevice* InputReader::createDeviceLocked(int32_t deviceId,        const String8& name, uint32_t classes) {    InputDevice* device = new InputDevice(&mContext, deviceId, name, classes);    ....    if (keyboardSource != 0) {        device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType));    }    // Cursor-like devices.    if (classes & INPUT_DEVICE_CLASS_CURSOR) {        device->addMapper(new CursorInputMapper(device));    }    // 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));    }    // Joystick-like devices.    if (classes & INPUT_DEVICE_CLASS_JOYSTICK) {        device->addMapper(new JoystickInputMapper(device));    }    return device;}

     總之,它呼叫createDeviceLocked建立一個InputDevice裝置,並根據class類別建立對應的事件轉換器(InputMapper),然後把這些新那建的InputMapper增加到InputDevice::mMappers中。InputMapper關係如下圖所示:

   2)呼叫InputDevice::configure配置此InputDevice

   3)呼叫InputDevice::reset重置此InputDevice

   4)把新建的InputDevice增加到InputReader::mDevices中。

   InputReader::processEventsLocked裝置增加、刪除處理總結:

     它負責處理Device 增加、刪除事件。增加事件的流程為:為一個新增的Device建立一個InputDevice,並增加到InputReader::mDevices中;根據新增加裝置的class類別,建立對應的訊息轉換器(InputMapper),然後此訊息轉換器加入InputDevice::mMappers中。訊息轉換器負責把讀取的RawEvent轉換成特定的事件,以供應用程式使用。

     EventHub與InputReader各自管理功能:

     1)EventHub管理一堆Device,每一個Device與Kernel中一個事件輸入裝置對應

     2)InputReader管理一堆InputDevice,每一個InputDevice與EventHub中的Device對應

     3)InputDevice管理一些與之相關的InputMapper,每一個InputMapper與一個特定的應用事件相對應,如:SingleTouchInputMapper。

4.3.1.1.2 事件驅動裝置事件處理processEventsForDeviceLocked

   下面的分析處理以單點觸控為例,對於單點觸控Touch Down時,它將報告以下事件:

    程式碼:

    input_report_abs(myInputDev, ABS_X, event->x);    input_report_abs(myInputDev, ABS_Y, event->y);

    產生的事件:*type, code, value                          EV_ABS,ABS_X,event->x                          EV_ABS,ABS_Y,event->y     

    程式碼: 

    input_report_key(myInputDev, BTN_TOUCH,  1);    產生的事件:*type, code, value                          EV_KEY, BTN_TOUCH, 1

     程式碼:

    input_sync(myInputDev);        它呼叫input_event(dev, EV_SYN, SYN_REPORT, 0);     產生的事件:*type, code, value                           EV_SYN, SYN_REPORT, 0

     它負責處理來自於同一個裝置且在mEventBuffer中連續的多個事件,其函式原型如下:

void InputReader::processEventsForDeviceLocked(int32_t deviceId,        const RawEvent* rawEvents, size_t count) {    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);    if (deviceIndex < 0) {        LOGW("Discarding event for unknown deviceId %d.", deviceId);        return;    }    InputDevice* device = mDevices.valueAt(deviceIndex);    if (device->isIgnored()) {        //LOGD("Discarding event for ignored deviceId %d.", deviceId);        return;    }    device->process(rawEvents, count);}

它其實很簡單,根據輸入的deviceId找到對應的InputDevice,然後呼叫InputDevice::process以對裝置輸入事件進行處理。InputDevice::process主要原始碼如下:

void InputDevice::process(const RawEvent* rawEvents, size_t count) {    // Process all of the events in order for each mapper.    // We cannot simply ask each mapper to process them in bulk because mappers may    // have side-effects that must be interleaved.  For example, joystick movement events and    // gamepad button presses are handled by different mappers but they should be dispatched    // in the order received.    size_t numMappers = mMappers.size();    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++)     {        for (size_t i = 0; i < numMappers; i++) {            InputMapper* mapper = mMappers[i];            mapper->process(rawEvent);        }    }}

       從上面的程式碼中可以看出,在InputDevice::process中,對於傳入的每一個RawEvent,依次呼叫InputDevice中的每一個InputMapper來進行處理。前面提到過,InputDevice包含一組處理對應裝置事件InputMapper,現在這些InputMapper開始幹活了。      下面以處理一個單點觸控事件裝置的事件為例,進行分析,其它的處理流程類似。對於mapper->process需要檢視InputReader::createDeviceLocked中建立的具體的InputMapper的process函式。下面就看看SingleTouchInputMapper的process是如何處理的,其程式碼如下:

void SingleTouchInputMapper::process(const RawEvent* rawEvent) {    TouchInputMapper::process(rawEvent);    mSingleTouchMotionAccumulator.process(rawEvent);}

1)TouchInputMapper::process

       由此可見,它將首先呼叫TouchInputMaaper::process處理此事件,其處理程式碼如下:

void TouchInputMapper::process(const RawEvent* rawEvent) {    mCursorButtonAccumulator.process(rawEvent);    mCursorScrollAccumulator.process(rawEvent);    mTouchButtonAccumulator.process(rawEvent);    if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) {        sync(rawEvent->when);    }}

1.1) mCursorButtonAccumulator.process(rawEvent)

     記錄mouse或touch pad按鍵狀態,記錄rawEvent->type為EV_KEY,且rawEvent->scanCode為BTN_LEFT、BTN_RIGHT、BTN_MIDDLE、BTN_BACK、BTN_SIDE、BTN_FORWARD、BTN_EXTRA、BTN_TASK的事件。

1.2) mCursorScrollAccumulator.process(rawEvent)

     記錄cursor scrolling motions,記錄rawEvent->type為EV_REL,且rawEvent->scanCode為REL_WHEEL、REL_HWHEEL的事件。

1.3) mTouchButtonAccumulator.process(rawEvent)

     記錄touch, stylus and tool buttons狀態,記錄rawEvent->type為EV_KEY,且rawEvent->scanCode為BTN_TOUCH、BTN_STYLUS、BTN_STYLUS2、BTN_TOOL_FINGER、BTN_TOOL_PEN、BTN_TOOL_RUBBER、BTN_TOOL_BRUSH、BTN_TOOL_PENCIL、BTN_TOOL_AIRBRUSH、BTN_TOOL_MOUSE、BTN_TOOL_LENS、BTN_TOOL_DOUBLETAP、BTN_TOOL_TRIPLETAP、BTN_TOOL_QUADTAP的事件。

     看到了吧,我們的BTN_TOUCH在這兒被處理了,且其value被儲存在mBtnTouch成員變數中。

1.4) sync(rawEvent->when)

      處理EV_SYN:SYN_REPORT,我們的EV_SYN就在這兒被處理了,當然它是Touch Down時,所發事件的最後一個事件。這兒才是處理的重點。

      TouchInputMapper::sync將呼叫SingleTouchInputMapper::syncTouch函式。

      a)SingleTouchInputMapper::syncTouch

      把mCurrentRawPointerData中的ABS_X和ABS_Y的值儲存在TouchInputMapper::mCurrentRawPointerData->pointers中。

          單點觸控的syncTouch一次處理一個RawEvent,在pointers中只有一個值;而多點觸控的syncTouch一次處理多個RawEvent,在pointers中有多個值,最多16個。

      b)TouchInputMapper::cookPointerData

      根據TouchInputMapper::mCurrentRawPointerData->pointers中的資料,通過計算,最後生成TouchInputMapper::mCurrentCookedPointerData.pointerCoords,mCurrentCookedPointerData.pointerProperties和mCurrentCookedPointerData.idToIndex的資料。把Raw進行cook,之後生成了cooked資料。

      c)TouchInputMapper::dispatchHoverExit

      d)TouchInputMapper::dispatchTouches

      d.a)它呼叫dispatchMotion

      d.b)在dispatchMotion中,根據cooked資料建立NotifyMotionArg物件,它描述了一個移動事件

      d.c)呼叫TouchInputMapper::getListener()->notifyMotion(&args)

              TouchInputMapper::getListener()呼叫mContext->getListener(),此mContext為InputReader::mContext,所以其getListener()返回的則為InputReader::mQueuedListener,則最後呼叫QueuedInputListener::notifyMotion

       補充1) InputReader::mContext在構造時用自己的指標初始化了mContext,從而mContext::mReader則為此InputReader例項。       補充2) 在InputReader::createDeviceLocked中建立InputDevice時,把自己的mContext作為引數傳入,從而把它儲存在InputDevice::mContext中;在建立InputMapper時,以InputDevice作為引數,且InputMapper把它儲存在mDevice中,然後從把InputDevice中的mContext也儲存在InputMapper的mContext中。

      d.d)把傳遞過來的NotifyMotionArg引數複製一份,然後加入QueuedInputListener::mArgsQueue例表中。

      e)TouchInputMapper::dispatchHoverEnterAndMove

2)mSingleTouchMotionAccumulator.process 

     記錄ABS相關的值,記錄rawEvent->type為EV_ABS,且rawEvent->scanCode為ABS_X、ABS_Y、ABS_PRESSURE、ABS_TOOL_WIDTH、ABS_DISTANCE、ABS_TILT_X、ABS_TILT_Y的事件。我們發的ABS_X和ABS_Y在這兒被處理了。

     事件處理相關資料結構如下圖所示:     

4.3.1.2 InputReader::mQueuedListener->flush()

      先溫習一下,至此的訊息結構變化流程:

     

      processEventsLocked已經把來自於事件裝置的事件煮熟之後放入到各種NotifyArgs(如NotifyMotionArgs)之中,然後把這些各種NotifyArgs加入InputReader::mQueuedListener::mArgsQueue連結串列中。本Flush函式就是要把mArgsQueue中的所有NotifyArgs進行處理。為描述方便,先看看其程式碼:

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

       看到了吧,確實很簡單,呼叫連結串列中每個NotifyArgs的notify函式,且有一個有意思的引數 mInnerListener,這個引數在前面多次提到過,它是在建立mQueuedListener時提供的,它其實就是InputManager中的mDispatcher,前面一直在InputReader中打轉轉,現在終於看到InputDispatcher登場了,說明事件很快就可以謝幕了。

       再向下看一下吧,這麼多類NotifyArgs,為描述方便,下面以NotifyMotionArgs為例,其程式碼為:

void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {    listener->notifyMotion(this);}

      下面就看看InputDispatcher(mDispatcher)的notifyMotion函式做了些什麼。這個InputDispatcher::notifyMotion(const NotifyMotionArgs* args)可就不簡單了。

       在InputDispatcher::notifyMotion中,       1)根據NotifyMotionArgs提供的資訊,構造一個MotionEvent,再呼叫mPolicy->filterInputEvent看是否需要丟棄此事件,如果需要丟棄則馬上返加。其中mPolicy為NativeInputManager例項,在構造InputDispatcher時提供的引數。

       2)對於AMOTION_EVENT_ACTION_UP或AMOTION_EVENT_ACTION_DOWN事件,則直接根據NotifyMotionArgs提供的資訊,構造一個MotionEntry。

       3)呼叫InputDispatcher::enqueueInboundEventLocked把新構造的MotionEntry新增到InputDispatcher::mInboundQueue中,並返回是否需要喚醒mLooper<向pipe中寫入資料>的標識。

      以上操作都是在InputReader執行緒中完成的,現在應該InputDispatcher執行緒開始工作了。

4. 4 分發輸入事件

InputDispatcherThread主迴圈如下:

Thread::_threadLoop->

   InputDispatcherThread::threadLoop->

      mDispatcher->dispatchOnce(InputDispatcher::dispatchOnce)->

          dispatchOnceInnerLocked then

          mLooper->pollOnce

下面先看看簡單的mLooper->pollOnce

 4.4.1 mLooper->pollOnce 

      其功能為等待超時或被pipe喚醒(InputReader執行緒呼叫InputDispatcher::notifyMotion時, InputDispatcher::notifyMotion根據情況呼叫mLooper->wake)。

      其呼叫流程如下:

      mLooper->pollOnce(int timeoutMillis)->

         Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData)->

4.4.2 dispatchOnceInnerLocked         

      1)從mInboundQueue從中依次取出EventEntry<MotionEntry的基類>

      2)呼叫InputDispatcher::dispatchMotionLocked處理此MotionEntry

      3)呼叫InputDispatcher::dispatchEventToCurrentInputTargetsLocked

            對於InputDispatcher::mCurrentInputTargets中的每一個InputTarget,並獲取對應的Connection,呼叫InputDispatcher::prepareDispatchCycleLocked,

其相關程式碼如下:

    for (size_t i = 0; i < mCurrentInputTargets.size(); i++) {        const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i);        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);        if (connectionIndex >= 0) {            sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex);            prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget,                    resumeWithAppendedMotionSample);        } else {#if DEBUG_FOCUS            LOGD("Dropping event delivery to target with channel '%s' because it "                    "is no longer registered with the input dispatcher.",                    inputTarget.inputChannel->getName().string());#endif        }    }

      4)InputDispatcher::prepareDispatchCycleLocked

           4.1)呼叫enqueueDispatchEntryLocked建立DispatchEntry物件,並把它增加到Connection::outboundQueue佇列中。

           4.2)呼叫activateConnectionLocked把當前Connection增加到InputDispatcher::mActiveConnections連結串列中

           4.3)呼叫InputDispatcher::startDispatchCycleLocked,接著它呼叫Connection::inputPublisher.publishMotionEvent來發布事件到ashmem buffer中,呼叫Connection::inputPublisher.sendDispatchSignal傳送一個dispatch訊號到InputConsumer通知它有一個新的訊息到了,快來消費吧!  關於消費者如何註冊和如何訊息的流程在下一個專題中再寫。本文到此結束!!!