[Android] 輸入系統(一)


Android輸入系統是人與機器交互最主要的手段。我們通過按鍵或者觸碰屏幕,會先經由linux產生中斷,進行統一的處理過后,轉換成Android能識別的事件信息,然后Android的輸入系統去獲取事件,分發給上層用戶程序進行處理。

下面在細分一下輸入事件在Android系統中的流程:

wpsC0C1.tmp

從圖上能看到,輸入事件有四個處理的地方:

  1. InputReaderThread
  2. InputDispatcherThread
  3. WindowInputEventReceiver
  4. handleReceiverCallback

 

 

上面四個地方按功能來划分,其中:

  1. InputReaderThread負責從輸入設備中獲取事件,事件加入inboundQueue隊列。
  2. InputDispatcherThread負責把inboundQueue中的事件信息取出,並且從系統中獲取該事件所需要分發到的目標(窗口),把事件與目標分別整合成分發項,把分發項加入outboundQueue。另外,這里還是事件的分發端,負責把outboundQueue中的事件取出,通過InputChannel進行分發。分發完成后把該事件入waitQueue。
  3. WindowInputEventReceiver是事件的接收端。事件會在這里被onTouch這類回調函數處理
  4. handleReceiveCallback用於接收處理過后的反饋信息,事件在WindowInputEventReceiver端被處理成功或者失敗,將會通過InputChannel返回Handled或者UNHandled消息。handleReceiveCallback接收到消息后將會對waitQueue中的事件進行出隊列處理。

 

 

InputManager

InputManager用於啟動InputReaderThread與InputDispatcherThread,會在system_server初始化的時候被創建並且調用InputManager的start方法啟動這兩個線程。

InputManager的構造函數如下:

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

可以看到構造了InputDispatcher與InputReader兩個類,這兩個類是功能類,分別為InputDispatcherThread與InputReaderThread提供功能。另外,在構建InputReader的時候,把mDispatcher傳遞了進去,用於構建QueueInputListener。在這里可以提前說明一下這個成員的作用:把輸入事件添加到inboundQueue。

 

 

構造函數最后調用了initialize,構建InputReaderThread、InputDispatcherThread。

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

InputManager的start用於啟動InputReaderThread與InputDispatcherThread這兩個線程。

status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }

    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputReader thread due to error %d.", result);

        mDispatcherThread->requestExit();
        return result;
    }

    return OK;
}

 

 

 

 

 

 

 

InputReaderThread

InputReaderThread是用來從輸入設備中讀取輸入事件的,首先看一下該線程的threadLoop函數

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

 

 

mReader即在構建InputReaderThread時傳進來的InputReader,負責實現讀取輸入事件所需要的各種功能。InputReader::loopOnce用於讀取一次輸入事件。其中,讀取一次包含三個主要動作:

  1. 獲取輸入事件
  2. 處理輸入事件
  3. 輸入數據flush

 

void InputReader::loopOnce() {
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
            processEventsLocked(mEventBuffer, count);
    }


    mQueuedListener->flush();
}

 

1. 獲取輸入事件getEvents

幾乎所有與輸入有關的事件都會從這里獲得。其中包含:

  1. EPOLL_ID_INORIFY.輸入設備打開或者刪除的事件
  2. EPOLL_ID_WAKE.管道發送過來的模擬事件
  3. EPOLL_IN.按鍵,觸摸這類實際操作事件

 

 

EPOLL_ID_INOTIFY,用於監控某個目錄(子目錄)下是否有新增或者刪除文件,在這里用於監視/dev/input,這個是輸入設備文件所在的目錄,如果有新增設備,則會在該目錄內創建新文件;如果刪除設備,則該目錄的相應文件會被刪除。

 

            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 (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
            mPendingINotify = false;
            readNotifyLocked();
            deviceChanged = true;
        }


status_t EventHub::readNotifyLocked() {
            if(event->mask & IN_CREATE) {
                openDeviceLocked(devname);
            } else {
                ALOGI("Removing device '%s' due to inotify event\n", devname);
                closeDeviceByPathLocked(devname);
            }
}

 

 

 

EPOLL_ID_WAKE,EventHub有維護一個pipe,當pipe的寫入端按照適當格式寫入時間后,getEvents可以通過pipe的讀取端獲取這個虛擬事件

 

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

 

 

EPOLL_IN,用於監控設備文件的輸入狀態,當我們按鍵或者觸摸設備時,我們就能獲得EPOLL_IN狀態,從而到該設備讀取輸入事件

            if (eventItem.events & EPOLLIN) {
                int32_t readSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);
                        event->when = now;
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;
                        capacity -= 1;
                    }

 

 

監聽事件用的是epoll_wait,由於epoll_wait一次能獲取的事件可能會有多個,所以一次的getEvents需要對所獲得的每個事件都進行上述代碼的打包操作,最后返回事件數組。

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

 

 

 

 

 

 

2. 處理輸入事件processEventsLocked

由getEvents獲得的事件數組會在這個函數內進行處理,其中事件數組中的事件大致可以分為兩類,在這個函數將他們分開處理

  1. 按鍵、觸摸事件
  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;
            }
#if DEBUG_RAW_EVENTS
            ALOGD("BatchSize: %d Count: %d", batchSize, count);
#endif
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            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;
            }
        }
        count -= batchSize;
        rawEvent += batchSize;
    }
}

 

 

在處理按鍵、觸摸事件時,會根據他們設備的類型調用不同的process函數進行處理。對於觸摸事件,基本上只是進行賦值,而按鍵事件則需要通過映射,把從設備文件讀取進來的值轉換成Android上層能統一處理的按鍵事件。

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    InputDevice* device = mDevices.valueAt(deviceIndex);
    device->process(rawEvents, count);
}


void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
    case EV_KEY: {
            if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) {
                keyCode = AKEYCODE_UNKNOWN;
                flags = 0;
            }
            processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);
        }
        break;
    }
}


 

 

上面的mapKey對按鍵進行了映射處理,processKey用於區分按鍵的按下或者松開。在processKey的最后,會把事件打包成NotifyKeyArgs,然后通過QueueInputListener把事件push進mArgQueue。由於這里是一個事件數組,所以mArgQueue是必須的。

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
        int32_t scanCode, uint32_t policyFlags) {

    if (down) {
    ...
    } else {
    ...
    }
    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
    getListener()->notifyKey(&args);
}


void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    mArgsQueue.push(new NotifyKeyArgs(*args));
}

 

 

 

 

 

3. 輸入數據flush

在事件數組都push進mArgQueue之后,就需要把mArgQueue隊列給推送出去進行下一步的操作,mQueuedListener->flush();就是負責進行隊列的推送。還記得我們最開始說的”在構建InputReader的時候,把mDispatcher傳遞了進去,用於構建QueueInputListener”,我們這里的flush最終就是調用了InputDispatcher的notifyKey

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

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

 

 

 

 

以notifyKey為例,其目的實際上是把事件隊列加入mInboundQueue,但是在入mInboundQueue隊列之前,調用了interceptKeyBeforeQueueing,該函數通過jni,調用到PhoneWindowManager的interceptKeyBeforeQueueing。而在入了mInboundQueue隊列后,就會調用wake函數去喚醒InputDispatcherThread。下一步就是InputDispatcherThread的工作了。

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    needWake = enqueueInboundEventLocked(newEntry);
    if (needWake) {
        mLooper->wake();
    }
}

 

 

 

InputDispatcherThread

InputDispatcherThread是用來進行事件分發的線程。內部也是調用InputDispatcher來實現所需要的功能。

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

 

 

每次分發,調用的都是dispatchOnce,其內部調用dispatchOnceInnerLocked進行分發后,線程會調用pollOnce進入睡眠,等待下次InputReaderThread的wake操作

void InputDispatcher::dispatchOnce() {
    dispatchOnceInnerLocked(&nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}

 

 

分發的過程可以大概分成以下幾個步驟:

  1. 從mInboundQueue的隊列頭取出事件
  2. 特殊事件的處理,如POLICY_FLAG_PASS_TO_USER這類事件能直接發送到用戶,類似於電量不足的這類事件:當電量低於20%時,直接往上層發送事件,而不用知道當前是在哪個Activity
  3. 一般事件的處理,進行分發

 

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    mPendingEvent = mInboundQueue.dequeueAtHead();

    // Poke user activity for this event.
    if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
        pokeUserActivityLocked(mPendingEvent);
    }

    switch (mPendingEvent->type) {
    case EventEntry::TYPE_KEY: {
        done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
        break;
    }
    }
}

 

 

分發事件,肯定需要知道事件要分發到哪里,即分發的目標窗口,不過目標窗口可能不止一個。

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
            entry, inputTargets, nextWakeupTime);
    
    // Dispatch the key.
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

 

 

由於可能存在多個目標窗口,所以需要對每個目標窗口都進行事件分發

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {
    for (size_t i = 0; i < inputTargets.size(); i++) {
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
    }
}

 

 

在分發前的准備,就是把事件入outboundQueue隊列,不過請注意,這里的隊列不同於inboundQueue,因為outboundQueue是窗口相關的,窗口跟InputDispatcherThread間建立起一個連接(connection),該outboundQueue就是connection的成員。

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) {
    // 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);
    }
}

void InputDispatcher::enqueueDispatchEntryLocked(
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
        int32_t dispatchMode) {
    // Enqueue the dispatch entry.
    connection->outboundQueue.enqueueAtTail(dispatchEntry);
}

 

 

在准備完成后就會調用startDispatchCycleLocked進行事件分發,startDispatchCycleLocked這個函數的主體是一個while循環,在循環體內會執行下面三個主要步驟:

  1. 調用connection的inputPublisher來發出事件
  2. 把事件從outboundQueue隊列中移除
  3. 把事件加入waitQueue隊列,當事件在處理完成后返回,就會從waitQueue中刪除該事件

 

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
    while (connection->status == Connection::STATUS_NORMAL
            && !connection->outboundQueue.isEmpty()) {
        DispatchEntry* dispatchEntry = connection->outboundQueue.head;

        switch (eventEntry->type) {
        case EventEntry::TYPE_KEY: {
            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);

            // Publish the key event.
            status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,
                    keyEntry->deviceId, keyEntry->source,
                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
                    keyEntry->keyCode, keyEntry->scanCode,
                    keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
                    keyEntry->eventTime);
        }
        }

        // Re-enqueue the event on the wait queue.
        connection->outboundQueue.dequeue(dispatchEntry);
        traceOutboundQueueLengthLocked(connection);
        connection->waitQueue.enqueueAtTail(dispatchEntry);
        traceWaitQueueLengthLocked(connection);
    }
}

 

 

我們來看一下inputPublisher的publishKeyEvent的實現,最后也是調用socket的send接口來實現。

status_t InputPublisher::publishKeyEvent(
        uint32_t seq,
        int32_t deviceId,
        int32_t source,
        int32_t action,
        int32_t flags,
        int32_t keyCode,
        int32_t scanCode,
        int32_t metaState,
        int32_t repeatCount,
        nsecs_t downTime,
        nsecs_t eventTime) {

    InputMessage msg;
    msg.header.type = InputMessage::TYPE_KEY;
    msg.body.key.seq = seq;
    msg.body.key.deviceId = deviceId;
    msg.body.key.source = source;
    msg.body.key.action = action;
    msg.body.key.flags = flags;
    msg.body.key.keyCode = keyCode;
    msg.body.key.scanCode = scanCode;
    msg.body.key.metaState = metaState;
    msg.body.key.repeatCount = repeatCount;
    msg.body.key.downTime = downTime;
    msg.body.key.eventTime = eventTime;
    return mChannel->sendMessage(&msg);
}

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

 

 

 

 

 

 

總體的流程如下

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM