天天看點

Android輸入系統源碼分析

參考:《深入了解Android 卷III》

 輸入事件的源頭是位于/dev/input/下的裝置節點,而輸入系統的終點是由WMS管理的某個視窗。最初的輸入事件為核心生成的原始事件,而最終傳遞給視窗的則是KeyEvent或MotionEvent對象。是以Android輸入系統的主要工作是讀取裝置節點中的原始事件,将其加工封裝,然後派發給一個特定的視窗以及視窗中的控件。這個過程由InputManagerService系統服務為核心的多個參與者共同完成。輸入系統的總體流程和參與者如圖所示。

Android輸入系統源碼分析
  • Linux核心,接受輸入裝置的中斷,并将原始事件的資料寫入到裝置節點中。
  • 裝置節點,作為核心與IMS的橋梁,它将原始事件的資料暴露給使用者空間,以便IMS可以從中讀取事件。
  • InputManagerService,一個Android系統服務,它分為Java層和Native層兩部分。Java層負責與WMS的通信。而Native層則是InputReader和InputDispatcher兩個輸入系統關鍵元件的運作容器。
  • EventHub,直接通路所有的裝置節點。并且正如其名字所描述的,它通過一個名為getEvents()的函數将所有輸入系統相關的待處理的底層事件傳回給使用者。這些事件包括原始輸入事件、裝置節點的增删等。
  • InputReader,是IMS中的關鍵元件之一。它運作于一個獨立的線程Reader線程中,通過其線程循環不斷地通過getEvents()函數從EventHub中将事件取出并進行處理。然後交給InputDispatcher進行派發。
  • InputDispatcher,是IMS中的另一個關鍵元件。它也運作于一個獨立的線程中Dispatcher線程中。InputDispatcher中保管了來自WMS的所有視窗的資訊,其收到來自InputReader的輸入事件後,會在其保管的視窗中尋找合适的視窗,并将事件派發給此視窗。
  • WMS,雖說不是輸入系統中的一員,但是它卻對InputDispatcher的正常工作起到了至關重要的作用。當建立視窗時,WMS為新視窗和IMS建立了事件傳遞所用的通道。另外,WMS還将所有視窗的資訊,包括視窗的可點選區域,焦點視窗等資訊,實時地更新到IMS的InputDispatcher中,使得InputDispatcher可以正确地将事件派發到指定的視窗。

以下是代碼分析,建議用sourceinsight進行代碼跟蹤。

輸入系統會建立Reader線程和Dispatcher線程

Reader線程和Dispatcher線程建立及啟動過程源碼分析:

SystemServer所有服務被建立的源頭,同其他系統服務一樣,IMS在SystemServer中被啟動。

SystemServer.java (frameworks\base\services\java\com\android\server)
private void startOtherServices() {
    inputManager = new InputManagerService(context);     //建立InputManagerService
    inputManager.start();                                //啟動這個服務
}
           
InputManagerService.java (frameworks\base\services\core\java\com\android\server\input)
public InputManagerService(Context context) {
    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());       //調用本地方法進行初始化  
}
public void start() {
    nativeStart(mPtr);             //調用本地nativeStart函數
}
           

調用native方法,建立Reader線程和Dispatcher線程并啟動

com_android_server_input_InputManagerService.cpp (frameworks\base\services\core\jni)    
static jlong nativeInit {
    /* 建立了一個NativeInputManager對象,此對象将是Native層元件與Java層IMS進行通信的橋梁 */
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, messageQueue->getLooper());
}
NativeInputManager::NativeInputManager {
    //NativeInputManager建立了EventHub,EventHub複雜的構造函數使其在建立後便擁有了監聽裝置節點的能力(Inotify和epoll機制)
    sp<EventHub> eventHub = new EventHub();

    // 接着建立了Native層的InputManager    
    mInputManager = new InputManager(eventHub, this, this);
}

被InputManagerService的start函數所調用來啟動線程。
static void nativeStart(JNIEnv* env, jclass clazz, jlong ptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    status_t result = im->getInputManager()->start();
}
           

InputManager統一建立并管理mReader、mReaderThread、mDispatcher、mDispatcherThread這四個執行個體化對象。

InputManager.cpp (frameworks\native\services\inputflinger)  
class InputManager {
    sp<InputReaderInterface> mReader;
    sp<InputReaderThread> mReaderThread;

    sp<InputDispatcherInterface> mDispatcher;
    sp<InputDispatcherThread> mDispatcherThread;
}
/* 在InputManager中建立InputDispatcher和InputReader執行個體化對象 */
InputManager::InputManager {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}
void InputManager::initialize() {
    //建立Reader線程和Dispatcher線程
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

status_t InputManager::start() {
    /* 啟動ReaderThread和DispatcherThread */
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
}
           

mDispatcherThread->run和mReaderThread->run分别會執行InputReaderThread和InputDispatcherThread線程的線程循環體。

InputReader.cpp (frameworks\native\services\inputflinger)   
/* 建立InputReaderThread線程,實作循環操作 */
bool InputReaderThread::threadLoop() {
    mReader->loopOnce();    // 執行InputReader的loopOnce()函數
    return true;
}
InputReaderThread啟動後,其線程循環将不斷地執行InputReader.loopOnce()函數。是以這個loopOnce()函數作為線程循環的循環體包含了InputReader的所有工作。

InputDispatcher.cpp (frameworks\native\services\inputflinger)   
bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}
           

至此,IMS的建立完成了。InputManager的建立過程分别為InputReader與InputDispatcher建立了承載它們運作的線程,此時start()函數的功能就是啟動這兩個線程,使得InputReader于InputDispatcher開始工作。

EventHub分析(Reader線程使用EventHub讀取事件):

getEvents讀取的輸入事件是一個RawEvent結構體             *       Linux驅動上報的輸入事件是input_event結構體
struct RawEvent {                                    *        struct input_event {                                      
    nsecs_t when;                                    *          struct timeval time;                                        
    int32_t deviceId;                                *          __u16 type;                                                  
    int32_t type;                                    *          __u16 code;                                                 
    int32_t code;                                    *          __s32 value;                                                
    int32_t value;                                   *        };                                                        
};                                                   *
                                                     *
type:                                               *        type:       
DEVICE_ADDED              //輸入裝置插入               *        EV_KEY
DEVICE_REMOVED            //輸入裝置拔掉               *        EV_ABS
FINISHED_DEVICE_SCAN      //有裝置上報了輸入事件        *        EV_REL
EV_KEY                                               *
EV_ABS                                               *
EV_REL                                               *
           

EventHub中使用RawEvent結構體是對input_event結構體的擴充,根據類型和源碼可知,EventHub使用inotify檢測/dev/input目錄下裝置的插入或者拔出,使用epoll檢測/dev/input/eventx有無資料。

EventHub.cpp (frameworks\native\services\inputflinger)
在EventHub的構造函數中使用inotify和epoll機制檢測/dev/input目錄下裝置的插入或者拔出
EventHub::EventHub(void){ 
    /* 初始化一個inotify句柄 */
    mINotifyFd = inotify_init();
    /* 将/dev/input目錄添加到句柄中 */
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);    
    //static const char *DEVICE_PATH = "/dev/input";

    mEpollFd = epoll_create(EPOLL_SIZE_HINT);
    /* 将inotify句柄添加到epoll的檢測清單中,開始檢測 */
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
}
           

擷取輸入事件 – getEvent函數

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    /* 這個循環是getEvents()函數的主體。在這個循環中,會先将可用事件放入到buffer中并傳回。
     如果沒有可用事件,則進入epoll_wait()等待事件的到來,epoll_wait()傳回後會重新循環将可用
     将新事件放入buffer */
    for (;;) {
        /* 掃描目錄下的所有裝置看是否有裝置插入或者删除 */
        scanDevicesLocked();
            status_t res = scanDirLocked(DEVICE_PATH);
                dir = opendir(dirname);
                while((de = readdir(dir)))
                    openDeviceLocked(devname) {
                        /* 打開/dev/input/eventx */
                        int fd = open(devicePath, O_RDWR | O_CLOEXEC);
                        /* 擷取版本号,GID等等 */
                        ioctl(fd, EVIOCGVERSION, &driverVersion)
                        ioctl(fd, EVIOCGID, &inputId)
                        ....

                        /* 将打開的檔案的fd添加到epoll檢測清單中,檢測是否有資料産生 */
                        epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)
                    }

        /* 如果EPOLL_ID_INOTIFY,說明/dev/input目錄有變化(增加或者删除裝置),mPendingEventIndex指定尚未
        處理的epoll_event的索引 */
        while (mPendingEventIndex < mPendingEventCount) {
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
                mPendingINotify = true;     //更改标記位為true
            }

            /* 如果EPOLLIN,說明有輸入事件産生,則去讀取輸入事件 */
           if (eventItem.events & EPOLLIN) {
              int32_t readSize = read(device->fd, readBuffer,sizeof(struct input_event) * capacity);    /* 讀取的輸入資料是input_event結構體 */

              /* 将讀取到的input_event結構體轉換為EventHub中對應的RawEvent結構體 */
              size_t count = size_t(readSize) / sizeof(struct input_event);
              for (size_t i = ; i < count; i++) {
                 event->type = iev.type;         //RawEvent* event;
                 event->code = iev.code;
                 event->value = iev.value;
                 event += ;
              }
           }
        }

        /* 如果标記位為true,則去讀取notify */
        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
            mPendingINotify = false;
            readNotifyLocked(); {
                //如果是插入了新的裝置,那麼就調用openDeviceLocked去打開這個裝置,然後擷取各種資訊,并添加到
                //檢測清單(上面分析過)
                if(event->mask & IN_CREATE) 
                openDeviceLocked(devname);  
            }
            deviceChanged = true;
         }

        // Report added or removed devices immediately.
        //裝置節點增删操作發生時,則重新執行循環體,以便将裝置變化的事件放入buffer中
        if (deviceChanged) {
           continue;
        }

        // 如果此次getEvents()調用成功擷取了一些事件,或者要求喚醒InputReader,則退出循環并
        // 結束getEvents()的調用,使InputReader可以立刻對事件進行處理
        if(event != buffer || awoken) {
            break;
        }

        /* 不斷的循環檢測/dev/input下的所有裝置epoll_wait()所取出的epoll_event存儲在mPendingEventItems
           中,mPendingEventCount指定了mPendingEventItems數組所存儲的事件個數。*/
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
    }  //end of for(;;)

  // All done, return the number of events we read.
  return event - buffer;
}
           

深入分析Reader線程

/*通過EventHub擷取事件清單。讀取的結果存儲在參數mEventBuffer中,傳回值表示事件的個數
  當EventHub中無事件可以抽取時,此函數的調用将會阻塞直到事件到來或者逾時 */
void InputReader::loopOnce() {
    /* 擷取輸入事件,參照下文EventHub分析 */
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    //如果有輸入事件發生,則調用processEventsLocked()函數對事件進行加工處理
    if (count) {
       processEventsLocked(mEventBuffer, count);
    }

    /*釋出事件。 processEventsLocked()函數在對事件進行加工處理之後,便将處理後的事件存儲在
    mQueuedListener中。在循環的最後,通過調用flush()函數将所有事件傳遞給InputDispatcher */
    mQueuedListener->flush();
}
           

對事件進行加工處理:

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    for (const RawEvent* rawEvent = rawEvents; count;) {
        /* 當有真正的輸入事件,則調用processEventsForDeviceLocked進行處理 */
        processEventsForDeviceLocked(deviceId, rawEvent, batchSize);

        /* 把所有的事件(插入新的輸入裝置/拔掉了一個輸入裝置/輸入裝置有輸入事件産生)都抽取出來,進行判斷 */
        switch (rawEvent->type) {
        case EventHubInterface::DEVICE_ADDED:
            /* 對于增加新裝置的時間,會在InputReader裡面構造一下input_device結構體進行記錄 */
            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;
      }
    }
}
           

具體處理

processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
{
    InputDevice* device = mDevices.valueAt(deviceIndex);
    device->process(rawEvents, count);  
    {
        InputMapper* mapper = mMappers[i];
        mapper->process(rawEvent);  
        {
            /* 對于鍵盤,調用Keyboard的process */
            void KeyboardInputMapper::process(const RawEvent* rawEvent)
            {
                /* 映射:scanCode是驅動程式上報的掃描碼  轉換為Android系統中的keyCode */
                getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags) 
                {//eventhub.cpp
                    /* 映射的時候會先使用kl檔案映射,如果失敗再使用kcm檔案映射 */
                    status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t* outKeycode, uint32_t* outFlags)   
                    }

                    /* 映射成功,則進行處理 */
                    processKey(rawEvent->when, rawEvent->value != , keyCode, scanCode, flags);
                    {
                        /* 根據參數構造args:down/up,keyCode scanCode newMetaState(是否有shift按下) */
                        NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
                        /* 然後通知listener來進行處理,把args發給dispatch線程 */
                        getListener()->notifyKey(&args);    
                    }
              } 
         }
    }
}
           

總結:Reader線程對輸入事件的處理很簡單,就是把驅動上報的scancode轉換為Android的AKEYCODE碼,然後構造成args參數,最後傳遞給dispatcher線程。

Reader線程到Dispatcher線程間資料的傳遞:

在分析InputManager.cpp的時候分析過,InputManager管理四個對象mReader mReaderThread mDispatcher mDispatcherThread

在InputManager的構造函數中建立mReader對象,其中第三個參數是一個listener:
/*
    InputReader::InputReader(const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& policy,
        const sp<InputListenerInterface>& listener)
 */
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);

是以getListener()->notifyKey(&args);中getListener()擷取的是一個mDispatcher對象,notifyKey是Dispatcher中的notifyKey方法.
           

Dispatcher線程分析:

接收資料

InputDispatcher.cpp (frameworks\native\services\inputflinger)   
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
      /* 将從Reader線程傳進來的args參數構造成一個KeyEvent結構體 */
      KeyEvent event;
      event.initialize(args->deviceId, args->source, args->action,
            flags, keyCode, args->scanCode, metaState, ,
            args->downTime, args->eventTime);

        /* 然後對傳入的輸入事件簡單處理 */
        mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
        {//NativeInputManager::interceptKeyBeforeQueueing
                       com_android_server_input_InputManagerService.cpp 
            /* 調用phonewindowmanager.java中的同名函數,傳回一個wmActions */
            wmActions = env->CallIntMethod(mServiceObj, gServiceClassInfo.interceptKeyBeforeQueueing, keyEventObj, policyFlags);
            {//interceptKeyBeforeQueueing  PhoneWindowManager.java 
                (frameworks\base\policy\src\com\android\internal\policy\impl)
                /* 如果是global按鍵,則直接傳回ACTION_PASS_TO_USER */
                if (mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event))
                    return result;   //result = ACTION_PASS_TO_USER;

                /* 對于systemkey 分類處理 */
                可以直接處理的話,處理它,設定傳回值 !ACTION_PASS_TO_USER,不要傳給使用者否則ACTION_PASS_TO_USER                 
                }

                /* 根據傳回值,設定policyFlags:POLICY_FLAG_PASS_TO_USER  */
                handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
                if (wmActions & WM_ACTION_PASS_TO_USER) {
                    policyFlags |= POLICY_FLAG_PASS_TO_USER;
        }

        /* 然後放入inbound隊列中 */
        needWake = enqueueInboundEventLocked(newEntry);

        if (needWake) {
            mLooper->wake();   //喚醒dispatch線程
        }
}
           

dispatch線程循環體分析

threadLoop() InputDispatcher.cpp 
mDispatcher->dispatchOnce();
{
    if (!haveCommandsLocked())    //沒有指令則從inbound隊列中取出事件,生成指令
       dispatchOnceInnerLocked(&nextWakeupTime);
       {
          mPendingEvent = mInboundQueue.dequeueAtHead();   //從inbound隊列中取出事件
          DropReason dropReason = DROP_REASON_NOT_DROPPED; //預設不丢棄

          對于:!POLICY_FLAG_PASS_TO_USER
          if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER))  
              dropReason = DROP_REASON_POLICY;      
                switch (mPendingEvent->type) 
                case EventEntry::TYPE_KEY: 
                    dispatchKeyLocked
                    {
                        // Clean up if dropping the event.
                        if (*dropReason != DROP_REASON_NOT_DROPPED) 
                            return true;    //傳回後會不休眠繼續執行dispatchOnce,處理下一個輸入事件
                    }

        對于:POLICY_FLAG_PASS_TO_USER
        switch (mPendingEvent->type) 
        case EventEntry::TYPE_KEY: 
            dispatchKeyLocked
            {
                if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
                    if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
                        //構造指令放入隊列
                        CommandEntry* commandEntry = postCommandLocked(                                           &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
                        if (mFocusedWindowHandle != NULL) {
                            commandEntry->inputWindowHandle = mFocusedWindowHandle;
                        }
                        commandEntry->keyEntry = entry;
                        entry->refCount += ;
                        return false; // wait for the command to run
                    }
                }

                /* 如果解析的結果是INTERCEPT_KEY_RESULT_CONTINUE,則尋找目标應用程式 */
                int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime);
                // Dispatch the key.   然後把事件發給應用程式
                dispatchEventLocked(currentTime, entry, inputTargets);
            }
       }

       if (runCommandsLockedInterruptible())  //運作指令
       {
          /* 取出指令,然後執行指令 */
          CommandEntry* commandEntry = mCommandQueue.dequeueAtHead();
          //指令即doInterceptKeyBeforeDispatchingLockedInterruptible
          Command command = commandEntry->command;    
          {//doInterceptKeyBeforeDispatchingLockedInterruptible
             /* 執行phonewindowmanager.java中的同名函數 */
            nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputWindowHandle, &event, entry->policyFlags);

            {//interceptKeyBeforeDispatching   PhoneWindowManager.java
            if (mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event))
                {//根據global_key.xml發送廣播給某個元件
                ComponentName component = mKeyMapping.get(keyCode);
                context.sendBroadcastAsUser(intent, UserHandle.CURRENT, null);  
                }
                return -;
            }

            /* 根據傳回值來設定解析的結果 */
            if (delay < )   //傳回-1, 不上傳給app  -- global key      system key
                entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_SKIP;
            else if (!delay)    //return 0,表示讓app處理  -- user key
                entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE;
            else
                entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER;
            entry->interceptKeyWakeupTime = now() + delay;
           }
       }
       nextWakeupTime = LONG_LONG_MIN;
}
           

分析到此結束,有誤的地方還請提出寶貴的意見!

最後再次感謝《深入了解Android 卷III》這本書,通過看這本書我學到了很多東西,同時,書中有很多語言組織的非常好,我就照搬過來了。