Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android系統源碼閱讀(11):Android的InputManagerService的工作過程

Android系統源碼閱讀(11):Android的InputManagerService的工作過程

編輯:關於Android編程

Android系統源碼閱讀(11):Android的InputManagerService的工作過程

請對照AOSP版本:6.0.1_r50。


1. 創建InputManager

這裡和老羅當年的版本有很大不同了,有了InputManagerService管理InputManager。

這裡寫圖片描述

1.1

想要探索如何啟動的相關server,需要從SystemServer開始探尋。從SystemServer的進程開始運行開始,它就會創建一些系統server,這裡就會啟動other services。

其中,會創建Input Manager和Window Manager兩個服務。

frameworks/base/services/java/com/android/server/SystemServer.java :

Slog.i(TAG, "Input Manager");
inputManager = new InputManagerService(context);

Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, inputManager,
        mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
        !mFirstBoot, mOnlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

1.2

先來仔細端詳一下InputManagerService的構造函數。這裡會調用c++層的初始化函數。
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java :

mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());

注意這裡將一個本地的MessageQueue傳遞到c++層。

1.3

這一步首先將java層的MessageQueue變為了c++的MessageQueue。然後構造了一個NativeInputManager對象,最後將指向該對象的指針im返回給java層。

frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp :

NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, messageQueue->getLooper());

1.4

在構造NativeInputManager時,會創建一個InputManager對象mInputManager。

frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp :

sp eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);

1.5

這一步會創建一個dispatcher負責分發輸入事件,一個reader負責獲取事件。
frameworks/native/services/inputflinger/InputManager.cpp :

mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();

1.6

這裡會創建兩個線程,在以後的步驟中會用來運行前面創建的dispathcer和reader。

frameworks/native/services/inputflinger/InputManager.cpp :

mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);

2. 啟動InputManager

將視線再次回到SystemServer中,在創建完InputManagerService後,需要將這個Service啟動,同樣是在1.1的startOtherServices函數裡,調用了InputManagerService的成員函數start。

這裡寫圖片描述

2.1

這裡首先調用了c++層的nativeStart,然後InputManagerService將自己交給Watchdog監視。然後注冊了PointerSpeedSetting和ShowTouchesSetting兩個Observer。

frameworks/base/services/core/java/com/android/server/input/InputManagerService.java :

nativeStart(mPtr);

// Add ourself to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);

registerPointerSpeedSettingObserver();
registerShowTouchesSettingObserver();

這兩個Observer暫時還沒搞清楚是干什麼的。

2.2

這一步將傳來的ptr參數轉化為一個NativeInputManager指針,同時開始啟動NativeInputManager中的InputManager。

frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp :

NativeInputManager* im = reinterpret_cast(ptr);
status_t result = im->getInputManager()->start();

2.3

這裡會啟動在1.6中創建的兩個線程,分別用來分發和監聽Input事件。

frameworks/native/services/inputflinger/InputManager.cpp :

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

3. 啟動InputDispatcher

在2.3中運行的線程以threadLoop為入口,開始進入循環。

這裡寫圖片描述

3.1

這一步直接將任務交給InputDispatcher的dispatchOnce函數。

frameworks/native/services/inputflinger/InputDispatcher.cpp :

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

3.2

整個函數如下:

frameworks/native/services/inputflinger/InputDispatcher.cpp :

void InputDispatcher::dispatchOnce() {

    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    {
        // acquire lock
        AutoMutex _l(mLock);
        mDispatcherIsAliveCondition.broadcast();

        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        if (!haveCommandsLocked()) {
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

        // Run all pending commands if there are any.
        // If any commands were run then force the next poll to wake up immediately.
        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;
        }
    } // release lock

    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}

在這一步驟中,首先判斷是否有Command還未被執行,如果有則不需要去獲取輸入事件,否則,會調用dispatchOnceInnerLocked函數去嘗試獲取事件,這裡會將nextWakeupTime傳遞過去,讓其設置合適的蘇醒時間,具體內容在以後講解。然後runCommandsLockedInterruptible函數會執行緩存的Command,如果有Command在這一步中被執行,則需要將蘇醒事件設置為LONG_LONG_MIN,因為執行這些命令需要耗費事件,在這期間可能已經有輸入事件發生了,所有下次循環不需要等待。

最後,根據等待時間和當前時間,計算出需要睡眠的時間,通過pollOnce進入睡眠,等待喚醒,或者超時。

3.3

這裡和上一個章節中的pollOnce道理相同。

3.4

這裡會調用epoll_wait函數,使其在mEpollFd所描述的epoll上等待一段時間,這個epoll監聽著pip的讀寫事件。如果有人在pip中寫入,則會返回,否則等待指定時間後返回。

system/core/libutils/Looper.cpp

int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);

4. 啟動InputReader

在2.3中運行的線程以threadLoop為入口,開始進入循環。

這裡寫圖片描述<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMyBpZD0="41">4.1

這一步與3.1一樣,將任務丟給InputReader處理。

4.2

這一步會嘗試從mEventHub中獲取事件,如果獲取一些事件,則進行處理。
frameworks/native/services/inputflinger/InputReader.cpp :

//從EventHub中獲取event,這裡先詳細講解這一步
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
//省略..
if (count) {
    //事件處理,將在後面博客中講解
    processEventsLocked(mEventBuffer, count);
}

這裡我們先考慮如何從EventHub中獲取事件的。

4.3

首先這一個函數不是就獲得一個event這麼簡單,它是想獲得一組event,這裡和舊版本有所不同,可見工程師對系統做了優化。這一步內容比較到,讓我們通過注釋來講解。

frameworks/native/services/inputflinger/EventHub.cpp :

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);
    AutoMutex _l(mLock);
    struct input_event readBuffer[bufferSize];
    //event 指向了存儲事件的位置
    RawEvent* event = buffer;
    size_t capacity = bufferSize;
    bool awoken = false;

    //開始循環獲取事件,目的是填充buffer
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

        // Reopen input devices if needed.
        //如果需要重新打開輸入設備,則首先要關閉所有的設備
        if (mNeedToReopenDevices) {
            mNeedToReopenDevices = false;
            ALOGI("Reopening all input devices due to a configuration change.");
            closeAllDevicesLocked();
            mNeedToScanDevices = true;
            break; // return to the caller before we actually rescan
        }

        // Report any devices that had last been added/removed.
        //這裡會移除所有關閉的設備
        while (mClosingDevices) {
            Device* device = mClosingDevices;
            ALOGV("Reporting device closed: id=%d, name=%s\n",
                 device->id, device->path.string());
            mClosingDevices = device->next;
            //創建了一個設備removed的event
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id;
            event->type = DEVICE_REMOVED;
            event += 1;
            delete device;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }

    //如果上面步驟關閉了設備,這裡需要重新掃描所有的設備
        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            //下面會詳細講解這裡如何獲取輸入設備的
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }

    //這裡會添加正在開啟的設備
        while (mOpeningDevices != NULL) {
            Device* device = mOpeningDevices;
            ALOGV("Reporting device opened: id=%d, name=%s\n",
                 device->id, device->path.string());
            mOpeningDevices = device->next;
            //同樣這裡會創建設備添加的event
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
            event->type = DEVICE_ADDED;
            event += 1;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }

        if (mNeedToSendFinishedDeviceScan) {
            mNeedToSendFinishedDeviceScan = false;
            event->when = now;
            event->type = FINISHED_DEVICE_SCAN;
            event += 1;
            if (--capacity == 0) {
                break;
            }
        }
    //以上步驟主要是負責重新獲取接入的設備,下面將會負責獲得設備中的event

        // Grab the next input event.
        bool deviceChanged = false;
        //開始循環獲取pending的event
        //當前處理的Event序號是否小於正在等待的事件數目,這裡會循環讀出所有等待的事件
        while (mPendingEventIndex < mPendingEventCount) {
        //獲取一個event項
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
        //如果這是個INotify事件
            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;
            }
        //如果這是一個Id wake事件,則讀出mWakeReadPipeFd的數據,讓等待在這個文件描述符上的線程得到喚醒 
            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;
            }

        //這裡開始處理其它非特殊的event
        //獲取event項對應的設備的編號
            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;
            }
            //獲取設備,所有已知的設備都放在了mDevices中
            Device* device = mDevices.valueAt(deviceIndex);
            if (eventItem.events & EPOLLIN) {
                //從這個設備中讀出數據流,並且存入readBuffer下
                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.
                //先處理一些異常情況,先省略
                //...
                } else { 
                    //鍵盤事件的id需要特殊處理,一直設置為0
                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;

                    size_t count = size_t(readSize) / sizeof(struct input_event);
                    //開始循環,從設備中讀出每一個event
                    for (size_t i = 0; i < count; i++) {
                        struct input_event& iev = readBuffer[i];

                        //這裡做了許多處理事件異常時間的工作,先略過
                        //...

                        //將獲取的事件存入event
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        //event指向下一個位置,容量也隨之減少一個
                        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.
                        //buffer已經填滿,退出處理pending event的循環,將index回到上一個位置,因為該設備event還沒讀完,下次再接著讀
                        mPendingEventIndex -= 1;
                        break;
                    }
                }
            } else if (eventItem.events & EPOLLHUP) {
              //處理一些其它情況,省略
              //...
            }
        }


        //在讀出所有event後,才能關閉設備,這裡省略了對此的處理過程
        //...

        //到這裡說明pending event已經處理完,或者buffer已經塞滿。buffer塞滿或者存了一些事件,則退出最外層填充buffer的循環
        // Return now if we have collected any events or if we were explicitly awoken.
        if (event != buffer || awoken) {
            break;
        }

        //這裡處理了一些wake lock的事情,省略
        //...

        //到這一步說明buffer裡沒有填任何事件,同時也沒有pending event
        //所以需要等待有人向pip裡寫入一些事件
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

        //時間已到,還是沒有事件,那咱就結束吧
        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.
            mPendingEventCount = size_t(pollResult);
            //獲取了一些event,那麼繼續循環,填充buffer!
        }
    }

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

4.4

從這裡開始掃描設備。在研究這個函數前,先看一下DEVICE_PATH的來頭:

frameworks/native/services/inputflinger/EventHub.cpp :

   //這一段代碼位於EventHub的構造函數中,這裡使用了linux的inotify機制
   //inotify機制可以監控文件的變化
   //因此系統可以實時監控設備的添加和移除
   mINotifyFd = inotify_init();
   int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);

frameworks/native/services/inputflinger/EventHub.cpp :

void EventHub::scanDevicesLocked() {
     //掃描目錄:/dev/input
    status_t res = scanDirLocked(DEVICE_PATH);
    if(res < 0) {
        ALOGE("scan dir failed for %s\n", DEVICE_PATH);
    }
    if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
        //創建一個虛擬鍵盤,這個虛擬設備就是利用inotify機制監控系統中輸入設備變化的
        createVirtualKeyboardLocked();
    }
}

4.5

這裡開始掃描/dev/input/目錄下的所有設備。

frameworks/native/services/inputflinger/EventHub.cpp :

status_t EventHub::scanDirLocked(const char *dirname)
{
    char devname[PATH_MAX];
    char *filename;
    DIR *dir;
    struct dirent *de;
    dir = opendir(dirname);
    if(dir == NULL)
        return -1;
    strcpy(devname, dirname);
    filename = devname + strlen(devname);
    //filename指向了devname目錄的尾端,方便在其後面添加設備文件
    *filename++ = '/';
    //讀取該目錄下的每一個設備文件
    while((de = readdir(dir))) {
        if(de->d_name[0] == '.' &&
           (de->d_name[1] == '\0' ||
            (de->d_name[1] == '.' && de->d_name[2] == '\0')))
            continue;
        strcpy(filename, de->d_name);
        //打開設備,devname裡是設備的絕對路徑
        openDeviceLocked(devname);
    }
    closedir(dir);
    return 0;
}

4.6

frameworks/native/services/inputflinger/EventHub.cpp :

status_t EventHub::openDeviceLocked(const char *devicePath) {
    char buffer[80];

    //打開文件
    int fd = open(devicePath, O_RDWR | O_CLOEXEC);
    if(fd < 0) {
        ALOGE("could not open %s, %s\n", devicePath, strerror(errno));
        return -1;
    }

    InputDeviceIdentifier identifier;

    // Get device name.
    if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) {
        //fprintf(stderr, "could not get device name for %s, %s\n", devicePath, strerror(errno));
    } else {
        buffer[sizeof(buffer) - 1] = '\0';
        identifier.name.setTo(buffer);
    }

    // Check to see if the device is on our excluded list
    //刪除不排除的設備

    //一下步驟從文件中獲取device的基本信息
    //...
    // Get device driver version.
    // Get device identifier.
    // Get device physical location.
    // Get device unique id.
    // Fill in the descriptor.
    // Make file descriptor non-blocking for use with poll().

    //創建device對象
    // Allocate device.  (The device object takes ownership of the fd at this point.)
    int32_t deviceId = mNextDeviceId++;
    Device* device = new Device(fd, deviceId, String8(devicePath), identifier);
    //根據device的特征,設置device的class參數
    //...

    // Register with epoll.
    struct epoll_event eventItem;
    memset(&eventItem, 0, sizeof(eventItem));
    eventItem.events = EPOLLIN;
    if (mUsingEpollWakeup) {
        eventItem.events |= EPOLLWAKEUP;
    }
    eventItem.data.u32 = deviceId;
    //將該device的文件fd交給epoll監視,以及時獲得它的變化
    if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
        ALOGE("Could not add device fd to epoll instance.  errno=%d", errno);
        delete device;
        return -1;
    }

    //處理時鐘問題..

    //添加device
    addDeviceLocked(device);
    return 0;

4.7

這一步比較輕松,將創建好的device對象放入mDevices即可。

void EventHub::addDeviceLocked(Device* device) {
    mDevices.add(device->id, device);
    device->next = mOpeningDevices;
    mOpeningDevices = device;
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved