framework 學(xué)習(xí)筆記21. input輸入事件番外4(事件讀取InputReader)

在上一節(jié) input輸入事件番外3 中講到 IMS 在 native 層的啟動,其中關(guān)于事件讀取線程循環(huán)讀取事件時調(diào)用了如下代碼:

// 循環(huán)執(zhí)行任務(wù)
bool InputReaderThread::threadLoop() {
    mReader->loopOnce();  // 循環(huán)執(zhí)行 mReader 的 loopOnce() 方法锈至;
    return true;
}

事件的讀取關(guān)鍵就是 InputReader 的 loopOnce() 方法剪况,接下來就從這個方法入手分析 InputReader 這個類以及它所要完成的任務(wù)吕粗;

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    Vector<InputDeviceInfo> inputDevices;
    { // acquire lock
        AutoMutex _l(mLock);

        oldGeneration = mGeneration;
        timeoutMillis = -1;

        uint32_t changes = mConfigurationChangesToRefresh;
        if (changes) {
            mConfigurationChangesToRefresh = 0;
            timeoutMillis = 0;
            refreshConfigurationLocked(changes);
        } else if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout);
        }
    } // release lock

    // 1. 讀取事件
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();

        if (count) {
            // 2. 事件的簡單處理试读;
            processEventsLocked(mEventBuffer, count);
        }

        if (mNextTimeout != LLONG_MAX) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
            if (now >= mNextTimeout) {
                mNextTimeout = LLONG_MAX;
                timeoutExpiredLocked(now);
            }
        }

        if (oldGeneration != mGeneration) {
            inputDevicesChanged = true;
            getInputDevicesLocked(inputDevices);
        }
    } // release lock

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    
    // 3. 把事件傳給 InputDispatcher 處理(也可以理解為交給分發(fā)線程處理)
    mQueuedListener->flush();  // 這個 mQueuedListener 其實(shí)就是 InputDispatcher
}

從上面的代碼中可以總結(jié)為三點(diǎn):
(1)讀取事件锨咙;
(2)事件的簡單處理十兢;
(3)把事件傳給 InputDispatcher 處理(也可以理解為交給分發(fā)線程處理)餐济;

1. 讀取事件

讀取事件是通過 mEventHub->getEvents() 方法獲取的耘擂,其基本原理在 Linux知識 中已經(jīng)介紹過,接下來簡單分析一下 EventHub絮姆。
1.1 EventHub 的構(gòu)造函數(shù):

EventHub::EventHub(void) :
        mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(),
        mOpeningDevices(0), mClosingDevices(0),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false), mNeedToScanDevices(true),
        mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) {
    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
    //(epoll 的用法第 1 步):epoll_create()
    mEpollFd = epoll_create(EPOLL_SIZE_HINT);  // EPOLL_SIZE_HINT 指定最大監(jiān)聽個數(shù)為 8

    // inotify 用法第 1 步:inotify_init()醉冤,初始化 fd
    mINotifyFd = inotify_init();
    // inotify 用法第 2 步:inotify_add_watch()秩霍,監(jiān)測;其中 DEVICE_PATH = "/dev/input";
    // inotify 用法第 3 步:read()蚁阳,讀攘迦蕖;在 getEvents() 中調(diào)用
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);  

    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    eventItem.data.u32 = EPOLL_ID_INOTIFY;  // 這里并沒有使用fd字段螺捐,而使用了自定義的值EPOLL_ID_INOTIFY
    //(epoll 的用法第 2 步):epoll_ctl()
    //(epoll 的用法第 3 步):epoll_wait()颠悬;在 getEvents() 中調(diào)用
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);  // 將對mINotifyFd的監(jiān)聽注冊到epoll對象中

    // ... 省略部分代碼 見注釋 1
}

在構(gòu)造函數(shù)中,使用了 inotify 機(jī)制監(jiān)測 "/dev/input" 目錄的變化定血;使用 epoll 監(jiān)測有無數(shù)據(jù)赔癌;大致流程如下:
(1)創(chuàng)建 inotify 的文件句柄 mINotifyFd = inotify_init() 并通過 inotify_add_watch() 監(jiān)測 "/dev/input" 目錄;
(2)在 openDeviceLocked() 中調(diào)用 fd = open(devicePath, O_RDWR | O_CLOEXEC) 打開各種設(shè)備節(jié)點(diǎn)澜沟,例如 /dev/input/event0灾票;
(3)使用 epoll_wait() 監(jiān)測這些文件句柄 mINotifyFd、fd茫虽;
(4)讀取文件句柄刊苍,構(gòu)造相應(yīng)的 RawEvent (下面有介紹),例如新增了設(shè)備節(jié)點(diǎn)就可以通過 mINotifyFd 監(jiān)測到濒析;

注釋1:在構(gòu)造函數(shù)省略的代碼中正什,EventHub 創(chuàng)建了一個名為 wakeFds 的匿名管道,并將管道讀取端的描述符的可讀事件注冊到 epoll 對象中号杏。因?yàn)?InputReader 在執(zhí)行 getEvents() 時會因無事件而導(dǎo)致其線程阻塞在 epoll_wait() 的調(diào)用里婴氮,然而有時希望能夠立刻喚醒 InputReader 線程使其處理一些請求。此時只需向 wakeFds 管道的寫入端寫入任意數(shù)據(jù)馒索,此時讀取端有數(shù)據(jù)可讀莹妒,使得epoll_wait() 得以返回,從而達(dá)到喚醒 InputReader 線程的目的绰上;

1.2 獲取事件:getEvents()
通過 mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE) 方法來獲取事件,其中 mEventBuffer 是一個大小為 256 的 RawEvent 數(shù)組渠驼,用于存放獲取到的事件蜈块;

static const int EVENT_BUFFER_SIZE = 256;
RawEvent mEventBuffer[EVENT_BUFFER_SIZE];

struct RawEvent {
    nsecs_t when;
    int32_t deviceId;
    int32_t type;  // 事件的類型
    int32_t code;
    int32_t value;
};

// RawEvent 的 type 類型:
enum {
        // Sent when a device is added.
        DEVICE_ADDED = 0x10000000,
        // Sent when a device is removed.
        DEVICE_REMOVED = 0x20000000,
        // Sent when all added/removed devices from the most recent scan have been reported.
        // This event is always sent at least once.
        FINISHED_DEVICE_SCAN = 0x30000000,

        FIRST_SYNTHETIC_EVENT = DEVICE_ADDED,
};

// 另外在掃描 "dev/input" 目錄下設(shè)備節(jié)點(diǎn)的過程中,中可以發(fā)現(xiàn)還有一下的一些事件類型:
    // openDeviceLocked() 中迷扇;
    // EV_KEY 0x01 按鍵事件  
    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);  
    // EV_ABS 0x03 絕對坐標(biāo)百揭,如觸摸屏上報的坐標(biāo)
    ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask); 
    // EV_REL 0x02 相對坐標(biāo),   如鼠標(biāo)上報的坐標(biāo) 
    ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
    // 用來描述具備兩種狀態(tài)的輸入開關(guān)
    ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask);
    // EV_LED 0x11 LED   
    ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask);
    // EV_FF 0x15 力反饋 
    ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask);
    ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask);
// 另外,還有  EV_SYN(0x00 同步事件)蜓席;SYN_REPORT(一次事件的結(jié)尾)  等等器一;
// 收到一個點(diǎn)之后并不會立即處理,而是一個事件完成之后才會處理厨内,SYN_REPORT就是這個事件的標(biāo)志祈秕。

接下來是 getEvents() 的分析:

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);
    AutoMutex _l(mLock);
    struct input_event readBuffer[bufferSize];
   // event指針指向了在 buffer 下一個可用于存儲事件的 RawEvent 結(jié)構(gòu)體渺贤。每存儲一個事件,event 指針都回向后偏移一個元素
    RawEvent* event = buffer;
   // capacity 記錄了 buffer 中剩余的元素數(shù)量请毛。當(dāng)capacity為0時志鞍,表示 buffer 已滿,此時需要停繼續(xù)處理新
   // 事件方仿,并將已處理的事件返回給調(diào)用者
    size_t capacity = bufferSize;
    bool awoken = false;

    // getEvents()的關(guān)鍵部分:在這個循環(huán)中固棚,會先將可用事件放入到buffer中并返回。如果沒有可用事件仙蚜,
    // 則進(jìn)入 epoll_wait() 等待事件的到來此洲,epoll_wait() 返回后會重新循環(huán)將可用將新事件放入 buffer
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

        // ... 省略部分代碼

        //(1)第一步:****************************** 見下方解釋 *****************************
        if (mNeedToScanDevices) {  // 稍稍注意下在 EventHub 構(gòu)造函數(shù)中 mNeedToScanDevices(true)
            mNeedToScanDevices = false;
            // 第一次進(jìn)入時掃描 "dev/input" 目錄下所有的設(shè)備節(jié)點(diǎn)時,會建立設(shè)備列表存儲在mDevice成員變量
            // 中 (EventHub中有設(shè)備列表 KeyedVector<int32_t, Device*> mDevices)委粉;并通過 open() 函
            // 數(shù)打開節(jié)點(diǎn)黍翎,最終調(diào)用 epoll_ctl() 添加到 epoll 中;
            scanDevicesLocked();  // 這個方法有興趣可以跟進(jìn)去看看
            mNeedToSendFinishedDeviceScan = true;
        }

        // ...

        if (mNeedToSendFinishedDeviceScan) {
            mNeedToSendFinishedDeviceScan = false;
            event->when = now;
            event->type = FINISHED_DEVICE_SCAN;
            event += 1;
            if (--capacity == 0) {
                break;
            }
        }

        // Grab the next input event.
        //(2)第二步:****************************** 見下方解釋 *****************************
        bool deviceChanged = false;
        // mPendingEventItems:處理未被 InputReader 取走的輸入事件與設(shè)備事件
        while (mPendingEventIndex < mPendingEventCount) {
           /* 在這里分析每一個 epoll_event艳丛,如果是表示設(shè)備節(jié)點(diǎn)可讀匣掸,則讀取原始事件并放置到 buffer
              中。如果是表示 mINotifyFd 可讀氮双,則設(shè)置 mPendingINotify 為 true碰酝,當(dāng) InputReader 
              將現(xiàn)有的輸入事件都取出后讀取 mINotifyFd 中的事件,并進(jìn)行相應(yīng)的設(shè)備加載與卸載操作戴差。
              另外送爸,如果此 epoll_event 表示 wakeFds 的讀取端有數(shù)據(jù)可讀,則設(shè)置 awake 標(biāo)志為 true暖释,
              無論此次 getEvents() 調(diào)用有無取到事件袭厂,都不會再次進(jìn)行 epoll_wait() 進(jìn)行事件等待 */
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
                if (eventItem.events & EPOLLIN) {
                    mPendingINotify = true;
                } else {
                    ALOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
                }
                continue;
            }

            if (eventItem.data.u32 == EPOLL_ID_WAKE) {
                if (eventItem.events & EPOLLIN) {
                    ALOGV("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 {
                    ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
                            eventItem.events);
                }
                continue;
            }

            ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
            if (deviceIndex < 0) {
                ALOGW("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.
                    ALOGW("could not get event, removed? (fd: %d size: %" PRId32
                            " bufferSize: %zu capacity: %zu errno: %d)\n",
                            device->fd, readSize, bufferSize, capacity, errno);
                    deviceChanged = true;
                    closeDeviceLocked(device);
                } else if (readSize < 0) {
                    if (errno != EAGAIN && errno != EINTR) {
                        ALOGW("could not get event (errno=%d)", errno);
                    }
                } else if ((readSize % sizeof(struct input_event)) != 0) {
                    ALOGE("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++) {
                        // input_event 是 linux kernel 獲取到的輸入事件
                        struct input_event& iev = readBuffer[i];

                        if (iev.type == EV_MSC) {
                            if (iev.code == MSC_ANDROID_TIME_SEC) {
                                device->timestampOverrideSec = iev.value;
                                continue;
                            } else if (iev.code == MSC_ANDROID_TIME_USEC) {
                                device->timestampOverrideUsec = iev.value;
                                continue;
                            }
                        }
                        if (device->timestampOverrideSec || device->timestampOverrideUsec) {
                            iev.time.tv_sec = device->timestampOverrideSec;
                            iev.time.tv_usec = device->timestampOverrideUsec;
                            if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
                                device->timestampOverrideSec = 0;
                                device->timestampOverrideUsec = 0;
                            }
                            ALOGV("applied override time %d.%06d",
                                    int(iev.time.tv_sec), int(iev.time.tv_usec));
                        }

#else
                        event->when = now;
#endif
                        // 將 input_event 封裝成 RawEvent
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;
                        capacity -= 1;
                    }
                    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 if (eventItem.events & EPOLLHUP) {
                ALOGI("Removing device %s due to epoll hang-up event.",
                        device->identifier.name.string());
                deviceChanged = true;
                closeDeviceLocked(device);
            } else {
                ALOGW("Received unexpected epoll event 0x%08x for device %s.",
                        eventItem.events, device->identifier.name.string());
            }
        }

        //(3)第三步:****************************** 見下方解釋 *****************************
        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
           /* 讀取 mINotifyFd 中的事件,同時對輸入設(shè)備進(jìn)行相應(yīng)的加載與卸載操作球匕。這個操作必須當(dāng)
              InputReader 將現(xiàn)有輸入事件讀取并處理完畢后才能進(jìn)行纹磺,因?yàn)楝F(xiàn)有的輸入事件可能來自需要
              被卸載的輸入設(shè)備,InputReader 處理這些事件依賴于對應(yīng)的設(shè)備信息 */
            mPendingINotify = false;
            readNotifyLocked();
            deviceChanged = true;
        }

        // Report added or removed devices immediately.
        // 設(shè)備節(jié)點(diǎn)增刪操作發(fā)生時亮曹,則重新執(zhí)行循環(huán)體橄杨,以便將設(shè)備變化的事件放入buffer中
        if (deviceChanged) {
            continue;
        }

        // Return now if we have collected any events or if we were explicitly awoken.
        // 如果此次getEvents()調(diào)用成功獲取了一些事件,或者要求喚醒InputReader照卦,則退出循環(huán)并
        // 結(jié)束getEvents()的調(diào)用式矫,使InputReader可以立刻對事件進(jìn)行處理
        if (event != buffer || awoken) {
            break;
        }

        //(4)第四步:****************************** 見下方解釋 *****************************
        // 此次getEvents()調(diào)用沒能獲取事件
        mPendingEventIndex = 0;

        mLock.unlock(); // release lock before poll, must be before release_wake_lock
        release_wake_lock(WAKE_LOCK_ID);

        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
        mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock

        if (pollResult == 0) {
            // Timed out.
            mPendingEventCount = 0;
            break;
        }

        if (pollResult < 0) {
            // An error occurred.
            mPendingEventCount = 0;

            // Sleep after errors to avoid locking up the system.
            // Hopefully the error is transient.
            if (errno != EINTR) {
                ALOGW("poll failed (errno=%d)\n", errno);
                usleep(100000);
            }
        } else {
            // Some events occurred.
            // 從 epoll_wait() 中得到新的事件后,重新循環(huán)役耕,對新事件進(jìn)行處理
            mPendingEventCount = size_t(pollResult);
        }
    }

    // All done, return the number of events we read.
    // 返回本次getEvents()調(diào)用所讀取的事件數(shù)量
    return event - buffer;
}

(1)第一步:首先進(jìn)行與設(shè)備相關(guān)的工作采转。如 EventHub 創(chuàng)建后第一次執(zhí)行 getEvents() 函數(shù)時,需要掃描 "/de/input" 目錄下所有設(shè)備節(jié)點(diǎn)并打開這些設(shè)備節(jié)點(diǎn)瞬痘。另外故慈,當(dāng)設(shè)備節(jié)點(diǎn)的發(fā)生增加時板熊,會將設(shè)備事件存入到 buffer 中;

(2)第二步:處理未被 InputReader 取走的輸入事件與設(shè)備事件惯悠。epoll_wait() 所取出的 epoll_event 存儲在mPendingEventItems中邻邮,mPendingEventCount 指定了 mPendingEventItems 數(shù)組所存儲的事件個數(shù)。而mPendingEventIndex 指定尚未處理的 epoll_event 的索引克婶;

(3)第三步:如果 mINotifyFd 有數(shù)據(jù)可讀筒严,說明設(shè)備節(jié)點(diǎn)發(fā)生了增刪操作;

(4)第四步:如果此次 getEvents() 調(diào)用沒能獲取事件情萤,說明 mPendingEventItems 中沒有事件可用鸭蛙,于是執(zhí)行 epoll_wait() 函數(shù)等待新的事件到來,將結(jié)果存儲到 mPendingEventItems 里筋岛,并重置 mPendingEventIndex 為 0娶视;

2. 事件的簡單處理

在 InputReader 中 loopOnce() 方法中調(diào)用 processEventsLocked(mEventBuffer, count) 會對獲取到的事件進(jìn)行簡單的處理,其復(fù)雜的處理及分發(fā)是在 InputDispatcher 中完成的睁宰;

// 根據(jù)獲取到的 RawEvent 的 type 類型進(jìn)行處理:
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        // 如果是常規(guī)的 event 事件肪获,F(xiàn)IRST_SYNTHETIC_EVENT = DEVICE_ADDED (0x10000000)
        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;
            }

            // processEventsForDeviceLocked() 處理一般的事件: 見 2.2 
            // 把這次獲取到的 event 數(shù)組中屬于同一批次的,進(jìn)一步處理柒傻,判定條件就是:常規(guī) event 以及是屬于同一設(shè)備 
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);  
        } 
        // 處理 3 種特殊的事件:例如添加設(shè)備孝赫, 見 2.1 
        else {  // 這里就是3種特殊的 event 類型邓厕,例如有時候打開設(shè)備的時候會有這個 ADD 事件
            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:
                ALOG_ASSERT(false); // can't happen
                break;
            }
        }
        // 如果在上面沒有處理完 event 數(shù)組中的成員社露,那么依次繼續(xù) 
        count -= batchSize;
        rawEvent += batchSize;
    }
}

2.1 添加設(shè)備:
既然是添加設(shè)備,那么必定有一個容器來保存這些設(shè)備映胁,這個容器就是 KeyedVector<int32_t, InputDevice* > mDevices预侯;還記得獲取輸入事件的時候致开,掃描 "dev/input" 目錄下設(shè)備節(jié)點(diǎn)時也有一個保存設(shè)備的容器 KeyedVector<int32_t, Device *> mDevices 嗎?在這里介紹一下兩者的區(qū)別:簡單來說就是里面的設(shè)備不同萎馅;

// EventHub 中的 Device:
struct Device {
        Device* next;
        // 文件句柄双戳;
        int fd; // may be -1 if device is virtual 
        // classes 信息,在 InputReader 中會用到校坑;
        uint32_t classes;
        // 設(shè)備信息拣技;
        const InputDeviceIdentifier identifier;  
        // 配置信息(例如 Linux 內(nèi)核獲取到的按鍵值1,在 Android 中經(jīng)過配置文件轉(zhuǎn)換并不是1耍目,而是其他值);
        String8 configurationFile; 
        PropertyMap* configuration;
        // ... 省略
}

// InputReader 中的 InputDevice:
class InputDevice {
private:
    InputReaderContext* mContext;
    int32_t mId;
    int32_t mGeneration;
    int32_t mControllerNumber;
    InputDeviceIdentifier mIdentifier;

    // 關(guān)注點(diǎn):這里有一個 InputMapper 的成員變量徐绑,它的作用是將各種封裝了各種不同的事件類型邪驮,例如
    // 鍵盤事件的設(shè)備對應(yīng)的是 KeyboardInputMapper;
    Vector<InputMapper*> mMappers;
    // ... 省略
}

這里就會有一個問題傲茄,為什么對于一個設(shè)備要進(jìn)行添加兩次毅访,這么做是不是多余的呢沮榜?在我看來,這么設(shè)計的目的應(yīng)該是一個分層設(shè)計(或者說單一職責(zé))的思想喻粹;在 EventHub 中處理的是比較原始的數(shù)據(jù)蟆融,主要是一些事件的獲取、設(shè)備的配置等工作守呜,而在 InputReader 中是對 EventHub 中獲取的一些事件進(jìn)行初步處理和傳遞事件發(fā)給 InputDispatcher 處理型酥,所以才這么封裝的;

那么查乒,接下來就分析一下添加設(shè)備的源代碼 addDeviceLocked():

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex >= 0) {
        ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
        return;
    }

    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
    uint32_t classes = mEventHub->getDeviceClasses(deviceId);
    int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);

    // 創(chuàng)建出 InputDevice弥喉;  詳見下面 createDeviceLocked() 的分析
    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
    device->configure(when, &mConfig, 0);
    device->reset(when);

    if (device->isIgnored()) {
        ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
                identifier.name.string());
    } else {
        ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId,
                identifier.name.string(), device->getSources());
    }

    mDevices.add(deviceId, device);  // 將創(chuàng)建出的 device 加入到 mDevices 中;
    bumpGenerationLocked();
}

創(chuàng)建 InputDevice:createDeviceLocked()

// 根據(jù) rawEvent 中的 deviceId 創(chuàng)建相應(yīng)的 InputDevice 并添加相應(yīng)的 Mapper
InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
        const InputDeviceIdentifier& identifier, uint32_t classes) {
    InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(),
            controllerNumber, identifier, classes);

    // External devices.
    if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
        device->setExternal(true);
    }

    // Switch-like devices.
    if (classes & INPUT_DEVICE_CLASS_SWITCH) {
        device->addMapper(new SwitchInputMapper(device));
    }

    // Vibrator-like devices.
    if (classes & INPUT_DEVICE_CLASS_VIBRATOR) {
        device->addMapper(new VibratorInputMapper(device));
    }

    // Keyboard-like devices.
    uint32_t keyboardSource = 0;
    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
    }
    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
    }
    if (classes & INPUT_DEVICE_CLASS_DPAD) {
        keyboardSource |= AINPUT_SOURCE_DPAD;
    }
    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
    }

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

2.2 事件初步處理:

// InputReader.cpp 中 processEventsForDeviceLocked() 方法:
void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex < 0) {
        ALOGW("Discarding event for unknown deviceId %d.", deviceId);
        return;
    }
    // 這里根據(jù) deviceId 取出上面添加進(jìn)去的 InputDevice
    InputDevice* device = mDevices.valueAt(deviceIndex);
    if (device->isIgnored()) {
        //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }

    device->process(rawEvents, count);
}


// InputReader.cpp 中 process() 方法:
void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    // 這里有個 mapper 數(shù)組玛迄,在 create 時 會根據(jù) classes 類型去匹配處理 mapper由境,一般都是匹配一個
    size_t numMappers = mMappers.size();

    // 遍歷事件數(shù)組,依次去處理  
    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
        ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld",
                rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
                rawEvent->when);

        if (mDropUntilNextSync) {
            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                mDropUntilNextSync = false;
                ALOGD("Recovered from input event buffer overrun.");
            } else {
                ALOGD("Dropped input event while waiting for next input sync.");
            }
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            ALOGI("Detected input event buffer overrun for device %s.", getName().string());
            mDropUntilNextSync = true;
            reset(rawEvent->when);
        } else {
            for (size_t i = 0; i < numMappers; i++) {
                // 調(diào)用 mapper 的 process 函數(shù)蓖议,交給 InputDispatcher 并開始分發(fā)流程虏杰;
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);  // 最終是調(diào)用了 設(shè)備相應(yīng)的 mapper->process(rawEvent) 方法;
            }
        }
    }
}

3. 事件傳遞給 InputDispatcher 處理:

事件經(jīng)過獲取勒虾,初步處理纺阔,最終執(zhí)行了 mapper->process(rawEvent) 方法,對于 mapper->process() 這個方法需要查看 InputReader::createDeviceLocked() 中創(chuàng)建的具體的InputMapper的process函數(shù)从撼。下面就以 SingleTouchInputMapper 的 process() 為例:

SingleTouchInputMapper 的 process() 方法:

// (1)
void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
    TouchInputMapper::process(rawEvent);  // 調(diào)用父類的process  
    mSingleTouchMotionAccumulator.process(rawEvent);
}

void TouchInputMapper::process(const RawEvent* rawEvent) {
    mCursorButtonAccumulator.process(rawEvent); 
    mCursorScrollAccumulator.process(rawEvent); 
    mTouchButtonAccumulator.process(rawEvent); 
    // 一組同步事件州弟,并且已經(jīng)完結(jié)(有事件結(jié)束標(biāo)值 SYN_REPORT);  
    if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
        sync(rawEvent->when);  // 關(guān)鍵方法低零,后續(xù)有分析婆翔;
    }
}
/**
以下語句均是將輸入事件信息轉(zhuǎn)存至類成員變量中:
    mCursorButtonAccumulator.process(rawEvent);  // 注釋 1 
    mCursorScrollAccumulator.process(rawEvent);   // 注釋 2
    mTouchButtonAccumulator.process(rawEvent);   // 注釋 3
    mSingleTouchMotionAccumulator.process(rawEvent);  // 注釋 4
**/

注釋1:mCursorButtonAccumulator.process(rawEvent)
記錄鼠標(biāo)或觸摸板的按鍵狀態(tài):記錄 rawEvent->type 為 EV_KEY,且rawEvent->code為BTN_LEFT掏婶、BTN_RIGHT啃奴、BTN_MIDDLE、BTN_BACK雄妥、BTN_SIDE最蕾、BTN_FORWARD、BTN_EXTRA老厌、BTN_TASK 事件瘟则。

注釋2:mCursorScrollAccumulator.process(rawEvent)
記錄光標(biāo)滾動:記錄rawEvent->type為EV_REL,且rawEvent->code為REL_WHEEL枝秤、REL_HWHEEL 事件醋拧。

注釋3:mTouchButtonAccumulator.process(rawEvent)
記錄觸摸 手寫筆 工具按鈕狀態(tài):記錄rawEvent->type為EV_KEY,且rawEvent->code為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 的數(shù)據(jù)在這里被處理了绰寞,且其 value 被保存在 mBtnTouch 成員變量中;

注釋4:mSingleTouchMotionAccumulator.process
記錄ABS相關(guān)的值铣口,記錄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 的數(shù)據(jù)在這里被處理了已艰。

TouchInputMapper 中的 sync(nsecs_t when)方法:

// 輸入事件分發(fā)的關(guān)鍵在 sync(nsecs_t when) 中:這個同步函數(shù)比較長痊末,下面是簡化后的代碼,重點(diǎn)事件的分發(fā)
void TouchInputMapper::sync(nsecs_t when) {
    // Sync button state.
    mCurrentButtonState = mTouchButtonAccumulator.getButtonState()
            | mCursorButtonAccumulator.getButtonState();

    // Sync scroll state.
    mCurrentRawVScroll = mCursorScrollAccumulator.getRelativeVWheel();
    mCurrentRawHScroll = mCursorScrollAccumulator.getRelativeHWheel();
    mCursorScrollAccumulator.finishSync();

    // Sync touch state.
    bool havePointerIds = true;
    mCurrentRawPointerData.clear();
    /*調(diào)用子類的 syncTouch哩掺,這里是 SingleTouchMotionAccumulator的syncTouch()凿叠,
      更新 ABS 坐標(biāo)值,這里是把數(shù)據(jù)存入到 mCurrentRawPointerData 中供下面 cook */
    syncTouch(when, &havePointerIds);  //這里是一個虛函數(shù)  

/***** 虛函數(shù)是一個類中希望重載的成員函數(shù) 嚼吞,通過指向派生類的基類指針或引用盒件,訪問派生類中同名覆蓋成員函數(shù) *****/

    // Reset state that we will compute below.
    mCurrentFingerIdBits.clear();
    mCurrentStylusIdBits.clear();
    mCurrentMouseIdBits.clear();
    mCurrentCookedPointerData.clear();  // 先清除一下
    if (mDeviceMode == DEVICE_MODE_DISABLED) {
        // Drop all input if the device is disabled.
        mCurrentRawPointerData.clear();
        mCurrentButtonState = 0;
    } else {
        // ...    
        // 這個函數(shù)很龐大,cook 數(shù)據(jù)舱禽,主要是生成 mCurrentCookedPointerData.pointerCoords炒刁,
        // mCurrentCookedPointerData.pointerProperties 和 mCurrentCookedPointerData.idToIndex
        cookPointerData();   
        if (mDeviceMode == DEVICE_MODE_POINTER) {
            PointerUsage pointerUsage = mPointerUsage;
            dispatchPointerUsage(when, policyFlags, pointerUsage);  
        } else {
            if (mDeviceMode == DEVICE_MODE_DIRECT
                    && mConfig.showTouches && mPointerController != NULL) {
                mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);                
                mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
                mPointerController->setButtonState(mCurrentButtonState);
                mPointerController->setSpots(mCurrentCookedPointerData.pointerCoords,
                        mCurrentCookedPointerData.idToIndex,
                        mCurrentCookedPointerData.touchingIdBits);
            }

            // 分發(fā)事件,這里的三個方法最終都回調(diào)用 dispatchMotion()
            dispatchHoverExit(when, policyFlags);
            dispatchTouches(when, policyFlags);
            dispatchHoverEnterAndMove(when, policyFlags);
        }

           // 之后的代碼是一些數(shù)據(jù)保存之類的操作
           // ...
    }
    // ...
    mCurrentRawVScroll = 0;
    mCurrentRawHScroll = 0;
}

事件傳遞給 InputDispatcher 處理 dispatchMotion():

void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
        int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
        const PointerProperties* properties, const PointerCoords* coords,
        const uint32_t* idToIndex, BitSet32 idBits,
        int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime) {
    // ...
    NotifyMotionArgs args(when, getDeviceId(), source, policyFlags,
            action, flags, metaState, buttonState, edgeFlags,
            mViewport.displayId, pointerCount, pointerProperties, pointerCoords,
            xPrecision, yPrecision, downTime);
    // 這里的 getListener() 其實(shí)就是 InputReader 的構(gòu)造函數(shù)中傳入的 mDispatcher 作為回調(diào)誊稚,只是做了進(jìn)一步封裝切心;
    getListener()->notifyMotion(&args);  
}

通過調(diào)用 dispatchMotion() 方法飒筑,事件最終交由 InputDispatcher 處理片吊,到這里本小節(jié)的內(nèi)容就結(jié)束了绽昏,下一節(jié)將繼續(xù) InputDispatcher 的學(xué)習(xí)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末俏脊,一起剝皮案震驚了整個濱河市全谤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌爷贫,老刑警劉巖认然,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異漫萄,居然都是意外死亡卷员,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門腾务,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毕骡,“玉大人,你說我怎么就攤上這事岩瘦∥次祝” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵启昧,是天一觀的道長叙凡。 經(jīng)常有香客問我,道長密末,這世上最難降的妖魔是什么握爷? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮严里,結(jié)果婚禮上新啼,老公的妹妹穿的比我還像新娘。我一直安慰自己田炭,他們只是感情好师抄,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著教硫,像睡著了一般叨吮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瞬矩,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天茶鉴,我揣著相機(jī)與錄音,去河邊找鬼景用。 笑死涵叮,一個胖子當(dāng)著我的面吹牛惭蹂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播割粮,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼盾碗,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了舀瓢?” 一聲冷哼從身側(cè)響起廷雅,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎京髓,沒想到半個月后航缀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡堰怨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年芥玉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片备图。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡灿巧,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出诬烹,到底是詐尸還是另有隱情砸烦,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布绞吁,位于F島的核電站幢痘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏家破。R本人自食惡果不足惜颜说,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望汰聋。 院中可真熱鬧门粪,春花似錦、人聲如沸烹困。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽髓梅。三九已至拟蜻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間枯饿,已是汗流浹背酝锅。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奢方,地道東北人搔扁。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓爸舒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親稿蹲。 傳聞我的和親對象是個殘疾皇子扭勉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容