Android Input(三)-InputReader獲取事件

原創(chuàng)內(nèi)容,轉(zhuǎn)載請注明出處,多謝配合。

上篇簡單交代了下輸入子系統(tǒng),那么這篇主要分析下InputReader獲取事件過程。

一、InputReader初始化

從前面初始化的介紹中,我們知道InputReader是在InputManager構(gòu)造方法中被初始化的。

frameworks/native/services/inputflinger/InputManager.cpp
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作為參數(shù)傳入了InputReader的構(gòu)造方法。
再看InputReader的構(gòu)造方法:

InputReader::InputReader(const sp<EventHubInterface>& eventHub,
       const sp<InputReaderPolicyInterface>& policy,
       const sp<InputListenerInterface>& listener) :
       mContext(this), mEventHub(eventHub), mPolicy(policy),
       mGlobalMetaState(0), mGeneration(1),
       mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
       mConfigurationChangesToRefresh(0) {
   mQueuedListener = new QueuedInputListener(listener);//這個(gè)listener對應(yīng)的就是InputDispatcher
   { // acquire lock
       AutoMutex _l(mLock);
       refreshConfigurationLocked(0);
       updateGlobalMetaStateLocked();
   } // release lock
}

InputReader的構(gòu)造函數(shù)中初始化了一個(gè)QueuedInputListener, 它接收InputListenerInterface作為它的參數(shù),從InputReader調(diào)用可知,這個(gè)InputListenerInterface其實(shí)就是InputDispatcher, QueueInputListener只是作為InputDispatcher的Wrapper.這里先埋個(gè)伏筆。

二、InputReader運(yùn)行

在InputManager執(zhí)行start的時(shí)候InputReaderThread->run()

frameworks/native/services/inputflinger/InputReader.cpp
bool InputReaderThread::threadLoop() {
   mReader->loopOnce();
   return true;
}

接著執(zhí)行 mReader->loopOnce();

void InputReader::loopOnce() {
   …
   //獲取事件
   size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
   { // acquire lock
       AutoMutex _l(mLock);
       mReaderIsAliveCondition.broadcast();
       if (count) {
           //處理事件
           processEventsLocked(mEventBuffer, count);
       }
   ...
   //傳遞事件
   mQueuedListener->flush();
}

這里主要做了三件事:獲取事件、處理事件、傳遞事件,下面分別來看看:
2.1 獲取事件

EventHub->getEvents上一篇已經(jīng)分析了,主要就是獲取kernel的event, 這里事件不僅包括input,還包括輸入設(shè)備的add/remove等相關(guān)事件。加入的輸入設(shè)備在沒有事件的時(shí)候,會(huì)block在EventHub中的epoll處,當(dāng)有事件產(chǎn)生后,epoll_wait就會(huì)返回,然后針對變化的事件進(jìn)行處理。

2.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
           //1、處理輸入事件
           processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
       } else {
           switch (rawEvent->type) {
           case EventHubInterface::DEVICE_ADDED://2、添加輸入設(shè)備
               addDeviceLocked(rawEvent->when, rawEvent->deviceId);
               break;
           case EventHubInterface::DEVICE_REMOVED://3、刪除輸入設(shè)備
               removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
               break;
           case EventHubInterface::FINISHED_DEVICE_SCAN://4、完成設(shè)備掃描
               handleConfigurationChangedLocked(rawEvent->when);
               break;
           default:
               ALOG_ASSERT(false); // can't happen
               break;
           }
       }
       count -= batchSize;
       rawEvent += batchSize;
   }
}

這里主要處理以上這4類事件,這里重點(diǎn)只關(guān)注下input event的收集流程:

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
  ...
    InputDevice* device = mDevices.valueAt(deviceIndex);//獲取到對應(yīng)的device
   ...
    device->process(rawEvents, count);//device執(zhí)行process操作
}

這里看到input的處理流程是由device執(zhí)行process發(fā)起的。

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
   size_t numMappers = mMappers.size();
    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
     ...
       if (mDropUntilNextSync) {
          ...
        } 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 {//對應(yīng)的EV_KEY type走如下流程
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);//對應(yīng)的InputMapper執(zhí)行process操作
            }
        }
    }
}

這里執(zhí)行的是mapper對應(yīng)的process,而mapper實(shí)際上就是對應(yīng)device中能匹配處理當(dāng)前event的具體執(zhí)行類。

void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
    case EV_KEY: {
        int32_t scanCode = rawEvent->code;
        int32_t usageCode = mCurrentHidUsage;
        mCurrentHidUsage = 0;
        if (isKeyboardOrGamepadKey(scanCode)) {
            processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
        }
        break;
    }
    case EV_MSC: {
        if (rawEvent->code == MSC_SCAN) {
            mCurrentHidUsage = rawEvent->value;
        }
        break;
    }
    case EV_SYN: {
        if (rawEvent->code == SYN_REPORT) {
            mCurrentHidUsage = 0;
        }
    }
    }
}

這里以案件事件EV_KEY為例,這里會(huì)執(zhí)行processKey方法

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode,
        int32_t usageCode) {
   …
     //調(diào)用eventhub的mapKey
    if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
                              &keyCode, &keyMetaState, &policyFlags)) {
    ...
    }
    ...
    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
    getListener()->notifyKey(&args);//參數(shù)打包成args發(fā)送給Listener
}

最終將event組合成了一個(gè)notifykeyArgs數(shù)據(jù)結(jié)構(gòu),同時(shí)調(diào)用了listener的notifykey方法。

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

這里將NotifyKeyArgs放入了一個(gè)ArgsQueue中。

這部分簡單來梳理一下:
InputReader收集不同設(shè)備輸入信息,而不同設(shè)備對事件的處理又有分類,因?yàn)榫唧wdevice的具體event交由對應(yīng)的mapper來處理,而不匹配的則忽略。而mapper的處理主要是將event按NotifyArgs數(shù)據(jù)結(jié)構(gòu)進(jìn)行封裝,并加入到一個(gè)ArgsQueue中。而NotifyArgs本身也是按事件類型來封裝的。


InputDevice與InputMapper關(guān)系圖
NotifyArgs的分類

2.3 傳遞事件

frameworks/native/services/inputflinger/InputListener.cpp

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

從ArgsQueue把事件取出來,分別調(diào)用它們自己的notify方法

以NotifyKeyArgs為例:

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

這個(gè)listener對應(yīng)的是mInnerListener,那么要想知道Args傳遞到哪去了,就確定下mInnerListener是什么就好了。而在之前的初始化介紹部分已經(jīng)埋過伏筆的,QueuedInputListener(listener);中傳入的這個(gè)listener對應(yīng)的就是InputDispatcher。

frameworks/native/services/inputflinger/InputListener.cpp

QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
       mInnerListener(innerListener) {
}

所以最終跟代碼發(fā)現(xiàn)innerListener實(shí)際上就是InputDispatcher,notifyKey()就是InputDispacher的成員函數(shù)

frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    ... 
    int32_t keyCode = args->keyCode;
    ...
    KeyEvent event;
    event.initialize(args->deviceId, args->source, args->action, flags, keyCode, args->scanCode, metaState, 0, args->downTime, args->eventTime); 
  …
  mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    ...
    int32_t repeatCount = 0;
    KeyEntry* newEntry = new KeyEntry(args->eventTime, args->deviceId, args->source, policyFlags,
           args->action, flags, keyCode, args->scanCode, metaState, repeatCount, args->downTime);
    needWake = enqueueInboundEventLocked(newEntry);
    if (needWake) {
        mLooper->wake();//如果需要被喚醒,則喚醒dispatcher對應(yīng)的Looper
    }
}

這里重點(diǎn)看兩個(gè)方法:interceptKeyBeforeQueueing 與 enqueueInboundEventLocked

void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent,
        uint32_t& policyFlags) {
    ...
        if (keyEventObj) { //通過jni方式回調(diào)java層
            wmActions = env->CallIntMethod(mServiceObj,
                    gServiceClassInfo.interceptKeyBeforeQueueing,
                    keyEventObj, policyFlags);
            if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) {
                wmActions = 0;
            }
            android_view_KeyEvent_recycle(env, keyEventObj);
            env->DeleteLocalRef(keyEventObj);
        } else {
            ALOGE("Failed to obtain key event object for interceptKeyBeforeQueueing.");
            wmActions = 0;
        }
    ...
    } 
}

這部分其實(shí)就是PhoneWindowManager先做一次事件攔截處理操作,也就解釋了為什么有些點(diǎn)擊事件是不會(huì)傳給應(yīng)用的,而是在PhoneWindowManager那里就已經(jīng)被攔截了。具體邏輯不鋪開分析。

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
   bool needWake = mInboundQueue.isEmpty();
   mInboundQueue.enqueueAtTail(entry);
   traceInboundQueueLengthLocked();
   switch (entry->type) {
   case EventEntry::TYPE_KEY: {
       KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
       ...
       break;
   }
   ...
   }
   return needWake;
}

這里將keyArgs轉(zhuǎn)換成KeyEntery這種數(shù)據(jù)封裝,并添加到InputDispatcher的mInboundQueue里,最后喚醒InputDispacher線程。

最后簡單總結(jié)一下整個(gè)InputReader獲取事件流程:
在InputManager執(zhí)行start的時(shí)候InputReaderThread->run(),接著會(huì)執(zhí)行 mReader->loopOnce();
內(nèi)部邏輯主要分三塊:
獲取事件:
getEvents時(shí),打開"/dev/input/"目錄下的input設(shè)備,并將其注冊到epoll的監(jiān)控隊(duì)列中。這樣一旦對應(yīng)設(shè)備上有可讀的input事件,則epool_wait()就會(huì)返回,并帶回deviceid,找到具體的device。整個(gè)事件的獲取中,除了input事件,設(shè)備的打開關(guān)閉等信息,也要包裝成event,上報(bào)給InputReader。

處理事件:
processEventsForDeviceLocked 調(diào)用對應(yīng)的InputDevice處理Input事件,而InputDevice又會(huì)去匹配上對應(yīng)的InputMapper來處理對應(yīng)事件。(在InputDevice中,存儲著許多InputMapper,每種InputMapper對應(yīng)一類Device,例如:Touch、Keyboard、Vibrator等等……)而調(diào)用InputDevice的process函數(shù),就是將Input事件傳遞給每一個(gè)InputMapper,匹配的InputMapper就會(huì)對Input事件進(jìn)行處理,不匹配的則會(huì)忽略。而對應(yīng)的InputMapper最終會(huì)按對應(yīng)的NotifyArgs數(shù)據(jù)結(jié)構(gòu)對事件進(jìn)行封裝,并加入到ArgsQueue中。

傳遞事件:
flush會(huì)將ArgsQueue中的每一個(gè)Arg取出來交由innerListener對應(yīng)的InputDispacher執(zhí)行對應(yīng)類型的notify方法,將keyArgs轉(zhuǎn)換成KeyEntery這種數(shù)據(jù)封裝,并添加到InputDispatcher的mInboundQueue里,最后喚醒InputDispacher線程。

最后放上整體流程圖以及時(shí)序圖


事件獲取流程大框架
事件獲取調(diào)用時(shí)序圖

到這里,整個(gè)的input事件的獲取就已經(jīng)完成了。

下一篇文章:
Android Input(四) -InputDispatcher分發(fā)事件

參考:
http://www.itdecent.cn/p/2bff4ecd86c9 借用兩張圖

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

友情鏈接更多精彩內(nèi)容