Android InputManager分析

本篇爲雞生蛋系列第二篇文章

  1. Linux input系統數據上報流程 https://segmentfault.com/a/11...
  2. Android InputManager分析

主要講一下inputmanager相關的,即驅動把數據上報到用戶空間後,用戶空間到應用這麼個流程,
在上一遍講內核的input子系統時候,我們採取的反向分析,即由驅動出發,最後到input core,input子系統架構這麼個由點到面的分析方法,
那分析inputmanager是否可採用這種方法如何呢?實際上,對於Android上層(Native framework/framework, c++/java)的分析,我一般
採用的是由上而下的分析,即從其初始化(main,構造,onFirstRef())開始, 通常在其初始化時候,會重一些很重要的上下層的連接,如果由下往上看,會麻煩點,
然後再結合實例,看看他的數據流向是如何的,或者一些重要的API, 例如對於Audio來說,可以結合播放音樂流程來分析整個系統架構。

簡單說來,input到應用的流程爲
EventHub監控並讀取/dev/input下數據 --> 給InputReader 加工處理 --> 到InputDispacher --> 找到focused窗口並通過input channel發出去

參考文檔:
十分鐘瞭解Android觸摸事件原理(InputManagerService)
https://www.jianshu.com/p/f05...
android控件系統:輸入事件在控件樹中的傳遞
https://blog.csdn.net/renshug...
https://blog.csdn.net/renshug...
InputManagerService分析一:IMS的啓動與事件傳遞
https://blog.csdn.net/lilian0...

相關代碼目錄:
Android 9.0 http://androidxref.com/9.0.0_r3/
frameworks/base/services/java/com/android/server/SystemServer.java
frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
frameworks/native/services/inputflinger/

1.1 初始化

frameworks/base/services/java/com/android/server/SystemServer.java
startOtherServices() {
    inputManager = new InputManagerService(context);
    ....
    wm = WindowManagerService.main(context, inputManager,
    ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
            /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
    ....
    inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
    inputManager.start();
    ......
}

IMS(InputManagerService)的初始化,是從SystemServer開始的,通過搜索代碼(如上),我們可以看到構造了一個實例,
並做爲參數傳給了WMS, 由此我們也猜想,會和WMS有緊密的關係,然後
IMS設置了setWindowManagerCallbacks()並通過start()函數啓動了,
SystemServer裏有關IMS的就這麼幾個地方,我們再看下構造和start()具體的流程,與WMS的關聯不分析。

frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
// Pointer to native input manager service object.
private final long mPtr;

public InputManagerService(Context context) {
    this.mContext = context;
    this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper());

    // config_useDevInputEventForAudioJack配置爲true, 耳機事件可通過input上報
    mUseDevInputEventForAudioJack =
            context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
......
    mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());
......
    LocalServices.addService(InputManagerInternal.class, new LocalService());
}

public void start() {
    Slog.i(TAG, "Starting input manager");
    nativeStart(mPtr);
....
}

InputManagerService構造和start()主要也是調到JNI的 nativeInit() nativeStart().

frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
        jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
....
    NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
            messageQueue->getLooper());
    im->incStrong(0);
    return reinterpret_cast<jlong>(im);
}

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
......
    status_t result = im->getInputManager()->start();
......
}

nativeInit()又構造了一個 NativeInputManager(),該類可認爲是上層JAVA和下層EventHub InputManager的橋樑,
nativeStart()通過 NativeInputManager最終調到 InputManager 的 start()方法

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject serviceObj, const sp<Looper>& looper) :
        mLooper(looper), mInteractive(true) {
......
    sp<EventHub> eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

NativeInputManager()的構造又new了 EventHub 和 InputManager , 其中
eventHub做爲參數傳給了 InputManager()

frameworks/native/services/inputflinger/EventHub.cpp
EventHub::EventHub(void) :
......{
    acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);

    mEpollFd = epoll_create(EPOLL_SIZE_HINT); // epoll機制
    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);

    mINotifyFd = inotify_init(); // inotify機制
    int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); // 利用inotify監控 DEVICE_PATH(/dev/input)創建和刪除
......
    eventItem.events = EPOLLIN;
    eventItem.data.u32 = EPOLL_ID_INOTIFY;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); // 將inotify的fd添加到Epoll監控中
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance.  errno=%d", errno);

    int wakeFds[2];
    result = pipe(wakeFds); //讀寫pipe, InputReader有事件時喚醒
    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);

    mWakeReadPipeFd = wakeFds[0];
    mWakeWritePipeFd = wakeFds[1];
......
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
......
}

EventHub相當於一個集線器,把底層的USB, TOUCH,鼠標等事件統一收集上來,再給上層。
其構造函數當中利用inotify機制監控"/dev/input" 目錄下設備的創建和刪除,這樣當有設備變更時就可以收到通知了,
構造函數也創建了所需要的mEpollFd,這個作爲IO多路複用的機制,不清楚的可以查下如何使用,
構造裏將mINotifyFd添加到了epoll裏,在後續input設備創建的時候,也會把input設備的fd添加進去,這樣當有數據或者設備變化時,
EventHub就可獲取這些事件,進一步處理。
構造還創建了兩個pipe,作爲wakeup的讀端和寫端,當InputReader.cpp有事件(配置變更,monitor, 超時請求等)喚醒EventHub處理。

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher); // eventHub又傳給了 InputReader,最終他們倆是緊密聯繫在一起的
    initialize(); // eventHub又傳給了
}

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

InputManager(),創建了InputDispatcher和InputReader實例並與對應的InputDispatcherThread InputReaderThread 線程關聯
具體的我們不往下跟了,有興趣的可以再看看,
至此,初始化流程告一段落。

InputManagerService.java的 start方法,最終到InputManager::start(),

status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
......
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
......
}

start() 方法目的就是讓這兩個線程跑起來,這樣就可以不斷的獲取,處理消息了。

1.2 小結

startOtherServices()/SystemServer.java
    + new InputManagerService(context) --> nativeInit(...) --> new NativeInputManager(...)
    +                                                              + new EventHub() --> inotify監控/dev/input + epoll + wake pipe
    +                                                              + new InputManager(eventHub,...)
    +                                                                  +  new InputDispatcher()
    +                                                                  +  new InputReader(eventHub,...)
    +                                                                  +  initialize()
    +                                                                          + new InputReaderThread(mReader)
    +                                                                          + new InputDispatcherThread(mDispatcher)
    +
    +                                                                                          ^
    +                                                                                          +
    + inputManager.start() --> nativeStart(mPtr) --> im->getInputManager()->start() --> mDispatcherThread->run() mReaderThread->run()

2. 讀取數據

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

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

上一小節講到IMS通過start()函數,最終讓InputReaderThread InputDispatcherThread兩個線程跑起來了,
線程跑起來後,他們因爲返回值爲true, 所以他們會不斷的loop, 即不斷的讀取,分發,讀取,分發……
看上面幾行代碼,覺得整個過程很簡單清晰,然而當我們繼續跟下去看細節的時候,你能 哇~~哇~~哇~~
這一節我們看看 mReader->loopOnce(), 下一節繼續看Dispatcher過程

void InputReader::loopOnce() {
......
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
......
        if (count) {
            processEventsLocked(mEventBuffer, count);
        }
......// 通知dispather分發
    mQueuedListener->flush();
}

InputReader的loopOnce()通過
EventHub getEvents()
獲得元數據,然後通過
processEventsLocked()
進一步的處理,
然後再通過
mQueuedListener->flush()
通知InputDispatcher有數據了,該處理了

2.1 InputReader::loopOnce()之 EventHub->getEvents()

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
......
   for (;;) {
       nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

       // Reopen input devices if needed.
......
       // Report any devices that had last been added/removed.
       while (mClosingDevices) {
......
       }

       // 掃描設備
       if (mNeedToScanDevices) {
           mNeedToScanDevices = false;
           scanDevicesLocked();
           mNeedToSendFinishedDeviceScan = true;
       }
......

       // Grab the next input event.
       bool deviceChanged = false;
       while (mPendingEventIndex < mPendingEventCount) {
           const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
......
           ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
......
           Device* device = mDevices.valueAt(deviceIndex);
           if (eventItem.events & EPOLLIN) { //epoll事件
               // 讀取數據
               int32_t readSize = read(device->fd, readBuffer,
                       sizeof(struct input_event) * capacity);
......
                       event->deviceId = deviceId; // <-- 設備id
                       event->type = iev.type;
                       event->code = iev.code;
                       event->value = iev.value;
                       event += 1;
......
       // Return now if we have collected any events or if we were explicitly awoken.
       if (event != buffer || awoken) {
           break;
       }

       // Poll for events.  Mind the wake lock dance!
......
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
......
    return event - buffer;
}

getEvents()會檢查是否需要掃描設備,如果需要的話,則會建立設備KeyedVector向量表,
之後若有數據到來則通過read()函數讀取數據, 返回RawEvent* buffer給processEventsLocked()進行下一步處理,
若啥事都沒有通過epoll_wait()阻塞等待。

本來數據的讀取(read())比較簡單, 這裏只列下設備掃描流程,作爲個人筆記,有興趣的可以看下

EventHub::scanDevicesLocked() --> scanDirLocked(DEVICE_PATH) "/dev/input" --> while處理 openDeviceLocked() -->

status_t EventHub::openDeviceLocked(const char *devicePath) {
......
    int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK);
......//一大堆ioctl的信息獲取
    if(ioctl(......)) {
......//生成唯一的 deviceId,和device, 做爲mdevices的 key, value. 以後的操作會常用到這個deviceId
    // 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);
......
    // Load the configuration file for the device.
    // 加載這個設備的 idc(Input Device Configuration)配置文件
    loadConfigurationLocked(device);

......//能力獲取和分類
    // Figure out the kinds of events the device reports.
    ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask);
    ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask);
    ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask);
...... // 設備分類
        device->classes |= ......;
......
    //加入到epoll當中
    if (registerDeviceForEpollLocked(device) != OK) {
......
    configureFd(device);
......//加入mDevices並更新 mOpeningDevices 鏈表
    addDeviceLocked(device);
    return OK;
}
// 對於我們的觸屏來說class爲
// See if this is a touch pad.
// Is this a new modern multi-touch driver?
if (test_bit(ABS_MT_POSITION_X, device->absBitmask)
        && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) {
......
    if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) {
        device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
    }
......//之後還會加載虛擬key.
// Configure virtual keys.
if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) {
    // Load the virtual keys for the touch screen, if any.
    // We do this now so that we can make sure to load the keymap if necessary.
    status_t status = loadVirtualKeyMapLocked(device);

status_t EventHub::loadVirtualKeyMapLocked(Device* device) {
    // The virtual key map is supplied by the kernel as a system board property file.
......
    path.append("/sys/board_properties/virtualkeys.");
    path.append(device->identifier.name);
......
    return VirtualKeyMap::load(path, &device->virtualKeyMap);

addDeviceLocked()即添加到
EventHub.h KeyedVector<int32_t, Device*> mDevices;
並更新鏈表mOpeningDevices

void EventHub::addDeviceLocked(Device* device) {
    mDevices.add(device->id, device);
    device->next = mOpeningDevices;
    mOpeningDevices = device;
}

另外要注意一點的是,在scanDevicesLocked()時候也會創建虛擬鍵盤。

void EventHub::scanDevicesLocked() {
    status_t res = scanDirLocked(DEVICE_PATH);
.......
    // 創建虛擬鍵盤
    if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) {
        createVirtualKeyboardLocked();
    }
}

2.2 InputReader::loopOnce()之 processEventsLocked

mEventHub->getEvents(), 反回元數據後,傳給 processEventsLocked()進一步處理
元數據的定義如下,主要記錄了時間,設備id, type, code, value.

struct RawEvent {
    nsecs_t when;
    int32_t deviceId;
    int32_t type;
    int32_t code;
    int32_t value;
};

其中的deviceId起了個連接作用,用於標識eventhub和iputreader中的設備,

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;
......
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
......
            case EventHubInterface::DEVICE_ADDED:
                addDeviceLocked(rawEvent->when, rawEvent->deviceId);
            case EventHubInterface::DEVICE_REMOVED:
            ......
            case EventHubInterface::FINISHED_DEVICE_SCAN:
            ......
    }
}

processEventsLocked()函數有個
processEventsForDeviceLocked() 對於對數據的處理,
另外還根據type, 處理了對設備添加移除,掃描的處理,
大家就有點奇怪了,咦,eventhub掃描設備的時候,不是有處理添加設備嗎?
咋這兒又有添加設備了? 而且看代碼,兩者都有個mDevices變量

EventHub.h KeyedVector<int32_t, Device*> mDevices;
InputReader.h KeyedVector<int32_t, InputDevice*> mDevices;

上面可看到兩者value類型不同,他們之間的key 即deviceID是相同的,
其實我個人認爲EventHub中的Device爲設備的本身屬性,是下層設備的實例化,
而InputReader中的InputDevice爲更高層次的抽象,主要用於往上層處理數據,
addDeviceLocked()過程中還會根據input設備的不同屬性設置不同的Mapper事件轉換器。

我們先看下processEventsForDeviceLocked()過程:

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

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    // Process all of the events in order for each mapper.
......
    // 可能會有多個mapper
    size_t numMappers = mMappers.size();
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
......
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);
            }
......
}

processEventsForDeviceLocked() --> device->process() --> mapper->process()
最終數據的處理也是通過mapper來處理的,所以我們還得看下mapper咋添加的

Mapper添加

mapper的添加是根據分類來添加的, 以觸屏爲例

frameworks/native/services/inputflinger/InputReader.cpp
InputReader::processEventsLocked() --> addDeviceLocked() --> createDeviceLocked() --> 
// 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));
}

所以觸屏的最後數據處理函數會調到
MultiTouchInputMapper的process函數不再詳細看

void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
    TouchInputMapper::process(rawEvent);

    mMultiTouchMotionAccumulator.process(rawEvent);
}

2.3 InputReader數據如何到InputDispatcher的?

InputReader::loopOnce() 數據處理完後便調用 mQueuedListener->flush() 通知 InputDispatcher 該處理數據了。

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

flush()方法即把mArgsQueue Vector一個個取出來,然後再調用notify()方法,
那我們肯定想要知道
1. 數據是咋壓入 mArgsQueue的?
2. notify() 後續流程咋把數據給到 InputDispatcher
1.

void QueuedInputListener::notifyConfigurationChanged(
        const NotifyConfigurationChangedArgs* args) {
    mArgsQueue.push(new NotifyConfigurationChangedArgs(*args));
}

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

void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    mArgsQueue.push(new NotifyMotionArgs(*args));
}

void QueuedInputListener::notifySwitch(const NotifySwitchArgs* args) {
    mArgsQueue.push(new NotifySwitchArgs(*args));
}

void QueuedInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
    mArgsQueue.push(new NotifyDeviceResetArgs(*args));
}

在QueuedInputListener中看到 notifyConfigurationChanged() notifyKey() notifyMotion() notifySwitch() notifyDeviceReset()
當有配置變化或事件時,都會新創建個notify args實例(都繼承自NotifyArgs),然後push到mArgsQueue,
以觸屏事件爲例,push流程爲:

TouchInputMapper::sync() --> processRawTouches() --> cookAndDispatch() --> dispatchTouches() --> dispatchMotion()
--> NotifyMotionArgs args(...) getListener()->notifyMotion(&args) -->
frameworks/native/services/inputflinger/InputListener.cpp
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    mArgsQueue.push(new NotifyMotionArgs(*args));
}
  1. InputReader::loopOnce() --> QueuedInputListener::flush() --> for args->notify(mInnerListener);

以觸屏NotifyMotionArgs爲例,其調用到

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

注意其 listener 爲 InputDispatcher (InputDispatcher 繼承自 InputDispatcherInterface class InputDispatcher : public InputDispatcherInterface),

frameworks/native/services/inputflinger/InputManager.cpp
mDispatcher = new InputDispatcher(dispatcherPolicy); mReader = new InputReader(..., ..., mDispatcher); --> InputReader::InputReader(..., ..., ...listener) --> new QueuedInputListener(listener);

所以最終就調到了
InputDispatcher::notifyMotion()

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
......// 合法性檢查
    if (!validateMotionEvent(args->action, args->actionButton,
                args->pointerCount, args->pointerProperties)) {
......// 預處理
    mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags);
......
        if (shouldSendMotionToInputFilterLocked(args)) {
            mLock.unlock();

            MotionEvent event;
            event.initialize(args->deviceId, args->source, args->action, args->actionButton,
                    args->flags, args->edgeFlags, args->metaState, args->buttonState,
                    0, 0, args->xPrecision, args->yPrecision,
                    args->downTime, args->eventTime,
                    args->pointerCount, args->pointerProperties, args->pointerCoords);

            policyFlags |= POLICY_FLAG_FILTERED;
            // 過濾
            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                return; // event was consumed by the filter
            }

            mLock.lock();
        }

        // Just enqueue a new motion event.
        MotionEntry* newEntry = new MotionEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, args->actionButton, args->flags,
                args->metaState, args->buttonState,
                args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
                args->displayId,
                args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0);

        // 入隊
        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock

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

notifyMotion()會先檢查合法性,然後預處理,如果需要過濾則進行過濾處理,
否則構建 MotionEntry,併入隊,隨後將looper喚醒。

bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
    bool needWake = mInboundQueue.isEmpty();
    // 入隊
    mInboundQueue.enqueueAtTail(entry);
......
}

InputReader這一側大至就分析完了,數據從InputReader傳到InputDispatcher也清楚了,
接下來看看數據分發。

3. 分發數據

在開頭也講到,InputDispatcherThread裏不斷的loop,調用dispatchOnce()進行數據的分發。

frameworks/native/services/inputflinger/InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {
......
        // Run a dispatch loop if there are no pending commands.
        // The dispatch loop might enqueue commands to run afterwards.
        if (!haveCommandsLocked()) {
            // 如果命令列隊爲空, 進行事件分發
            dispatchOnceInnerLocked(&nextWakeupTime);
        }
    //如果looper裏沒有信息,會阻塞,直到timeoutMillis超時
    mLooper->pollOnce(timeoutMillis);
}

dispatchOnce()裏如果命令處理完了,纔會調用dispatchOnceInnerLocked()進行事件處理。

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
.....//如果沒有event,抓取一個
  // Ready to start a new event.
  // If we don't already have a pending event, go grab one.
  if (! mPendingEvent) {
      if (mInboundQueue.isEmpty()) {
......
      } else {
          // Inbound queue has at least one entry.
          mPendingEvent = mInboundQueue.dequeueAtHead(); //<---從mInboundQueue隊頭抓個
          traceInboundQueueLengthLocked();
      }
......//一些錯誤處理,包括anr時間重置,略過

    switch (mPendingEvent->type) {
    case EventEntry::TYPE_CONFIGURATION_CHANGED: ....
    case EventEntry::TYPE_DEVICE_RESET: ....
    case EventEntry::TYPE_KEY: .....
......//對我們的touch來說是motion事件
    case EventEntry::TYPE_MOTION: {
        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
        if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
            dropReason = DROP_REASON_APP_SWITCH;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED
                && isStaleEventLocked(currentTime, typedEntry)) {
            dropReason = DROP_REASON_STALE;
        }
        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            dropReason = DROP_REASON_BLOCKED;
        }
        //分發事件
        done = dispatchMotionLocked(currentTime, typedEntry,
                &dropReason, nextWakeupTime);
        break;
    }
......
}

dispatchOnceInnerLocked從mInboundQueue隊列中取出之前的MotionEntry,
然後錯誤處理,對於觸屏事件做dispatchMotionLocked()

bool InputDispatcher::dispatchMotionLocked(
        nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
......// 是否爲 point event
    bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER;

    // Identify targets.
    Vector<InputTarget> inputTargets;
......
    if (isPointerEvent) {
        // Pointer event.  (eg. touchscreen)
        injectionResult = findTouchedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
    } else {
        // Non touch event.  (eg. trackball)
        injectionResult = findFocusedWindowTargetsLocked(currentTime,
                entry, inputTargets, nextWakeupTime);
    }
......
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

對於point event,會先用
findTouchedWindowTargetsLocked() 找到目標窗口,否則用
findFocusedWindowTargetsLocked() 找到目標窗口
對我們的觸屏來說,包含有該屬性
frameworks/native/include/android/input.h
AINPUT_SOURCE_TOUCHSCREEN = 0x00001000 | AINPUT_SOURCE_CLASS_POINTER,
找到目標窗口後,再用 dispatchEventLocked() 發給目標窗口

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

        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {
            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
......
}

void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
......
    // 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);
    }
}

enqueueDispatchEntryLocked()會根據flag mode進行比較,然後加入到connection的outboundQueue裏
connection->outboundQueue.enqueueAtTail(dispatchEntry);
然後再調用
startDispatchCycleLocked()最終通過socket把事件發出去

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
......
        case EventEntry::TYPE_MOTION: {
......
            // Publish the motion event.
            status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq,
                    motionEntry->deviceId, motionEntry->source, motionEntry->displayId,
                    dispatchEntry->resolvedAction, motionEntry->actionButton,
                    dispatchEntry->resolvedFlags, motionEntry->edgeFlags,
                    motionEntry->metaState, motionEntry->buttonState,
                    xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision,
                    motionEntry->downTime, motionEntry->eventTime,
                    motionEntry->pointerCount, motionEntry->pointerProperties,
                    usingCoords);
            break;
        }
......

frameworks/native/libs/input/InputTransport.cpp
status_t InputPublisher::publishMotionEvent(
......

    InputMessage msg;
    msg.header.type = InputMessage::TYPE_MOTION;
...... // 通過socket發送
    return mChannel->sendMessage(&msg);
}

4. 數據接收

數據發送後,又被誰接收到了呢?之後流程又如何呢?
input數據主要有兩種,一個應用,一個MonitoringChannel,
這裏僅簡單的列舉下,詳細的請看看參考文檔

4.1 App 接收

對於應用的接收,需要看input channel是咋建立的,
然後看看findTouchedWindowTargetsLocked(),咋找到目錄窗口,該函數很複雜,
但有個比較重要的是查詢 mWindowHandles, 該變量在setInputWindows()設置,

WindowManagerService.java
mInputMonitor.updateInputWindowsLw(false /*force*/); in addWindow()                    -+
mInputMonitor.updateInputWindowsLw(true /*force*/); in postWindowRemoveCleanupLocked()  +
mInputMonitor.updateInputWindowsLw(true /*force*/); in relayoutWindow()                 +
mInputMonitor.updateInputWindowsLw(true /*force*/); in relayoutWindow()                 +--> updateInputWindowsLw() -->
mInputMonitor.updateInputWindowsLw(true /*force*/); in removeWindowToken()              +
mInputMonitor.updateInputWindowsLw(true /*force*/); in startPositioningLocked()         +
mInputMonitor.updateInputWindowsLw(true /*force*/); in startPositioningLocked()         +
mInputMonitor.updateInputWindowsLw(true /*force*/); in finishPositioning()             -+

InputMonitor.java updateInputWindowsLw()
    +--> mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag)
        +--> updateInputWindows() 
            +--> InputManagerService.java setInputWindows() 
                +--> nativeSetInputWindows()
                    +--> im->setInputWindows (NativeInputManager::setInputWindows()) 
                        +--> mInputManager->getDispatcher()->setInputWindows() 
                             +-->
void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
......
        mWindowHandles = inputWindowHandles;

應用添加窗口設置mWindowHandles如上。在addWindow() relayoutWindow()...過程中都可能設置該變量

frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
......          // addToDisplay() 將調用WMS mService.addWindow()
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
...... //
                mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                        Looper.myLooper());
            }

對應用來說在setView() 時會調用mWindowSession.addToDisplay(),很後調用addWindow(), 然後win.openInputChannel(outInputChannel)等建立channel操作
addToDisplay() 之會,會將mInputChannel looper,通過 WindowInputEventReceiver綁在一起,
這樣當有數據到來時在looper裏面處理。

WindowInputEventReceiver()流程如下

WindowInputEventReceiver() --> InputEventReceiver.java InputEventReceiver() --> nativeInit() -->
frameworks/base/core/jni/android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
......
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    status_t status = receiver->initialize();
......
}
initialize() (NativeInputEventReceiver::initialize())--> setFdEvents(ALOOPER_EVENT_INPUT) -->
// 注意事件類型爲 ALOOPER_EVENT_INPUT
void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);

system/core/libutils/Looper.cpp
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
......// 將input channel的fd加入到epoll監控中
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);

** 當socket接收到數據時,通過handle來處理

android_view_InputEventReceiver.cpp
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
......// ALOOPER_EVENT_INPUT 事件
    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }
......
}
handleEvent() --> consumeEvents() -->
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
......
        status_t status = mInputConsumer.consume(&mInputEventFactory, //取數據
......
            if (inputEventObj) {
...
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj, //調用 dispatchInputEvent()
......
}
-->
frameworks/base/core/java/android/view/InputEventReceiver.java dispatchInputEvent() --> onInputEvent() --> 
final class WindowInputEventReceiver extends InputEventReceiver {
......
    @Override
    public void onInputEvent(InputEvent event, int displayId) {
        enqueueInputEvent(event, this, 0, true);
    }

void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
......
    if (processImmediately) {
        doProcessInputEvents(); // -->  deliverInputEvent(q);
    } else {
        scheduleProcessInputEvents();
    }
}
ViewRootImpl.java
private void deliverInputEvent(QueuedInputEvent q) {
......
    InputStage stage;
    if (q.shouldSendToSynthesizer()) {
        stage = mSyntheticInputStage;
    } else {
        // mFirstPostImeInputStage = earlyPostImeStage; new EarlyPostImeInputStage(nativePostImeStage); 注意參數爲nativePostImeStage,在 apply(q, onProcess(q)) 返回forward時會用到
        stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
    }
......
    if (stage != null) {
        handleWindowFocusChanged();
        stage.deliver(q);
......
}
stage.deliver --> apply(q, onProcess(q)) --> EarlyPostImeInputStage onProcess() --> processPointerEvent() --> (EarlyPostImeInputStage-->NativePostImeInputStage-->ViewPostImeInputStage-->SyntheticInputStage;)
ViewPostImeInputStage mView.dispatchPointerEvent(event)
View.java dispatchPointerEvent() +-->View.java dispatchTouchEvent() --> li.mOnTouchListener.onTouch(this, event) onTouchEvent(event)
                                 +
                                 +--> ViewGroup.java dispatchTouchEvent()

4.2 接收

在分發input數據時,會把 mMonitoringChannels 加入到目標中,然後通過socket也發給該目標,
InputDispatcher::dispatchMotionLocked() --> addMonitoringTargetsLocked() --> for mMonitoringChannels
在WMS時構造,會通過monitorInput()創建,
之後別的服務可通過WMS registerPointerEventListener() unregisterPointerEventListener() 以listener方式獲取數據

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
private WindowManagerService(......) {
......
    if(mInputManager != null) {
        final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM);
        mPointerEventDispatcher = inputChannel != null
                ? new PointerEventDispatcher(inputChannel) : null;

@Override
public void registerPointerEventListener(PointerEventListener listener) {
    mPointerEventDispatcher.registerInputEventListener(listener);
}

@Override
public void unregisterPointerEventListener(PointerEventListener listener) {
    mPointerEventDispatcher.unregisterInputEventListener(listener);
}

monitorInput()流程如下:

InputManagerService.java monitorInput()
    +--> nativeRegisterInputChannel(......, true);
        +--> NativeInputManager::registerInputChannel() 
            +--> mInputManager->getDispatcher()->registerInputChannel() -->
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
......
        if (monitor) {
            mMonitoringChannels.push(inputChannel);
        }
......
}

個人筆記

數據的轉存

從slot --> RawPointerData --> cookAndDispatch() cookPointerData()進一步處理將值給
mCurrentCookedState.cookedPointerData,主要爲
cookedPointerData.pointerCoords cookedPointerData.pointerProperties
dispatchMotion()時參數傳入cookedPointerData,進一步將數據封裝爲
NotifyMotionArgs

dispatchMotion(when, policyFlags, mSource,
......

   mCurrentCookedStat.cookedPointerData.pointerProperties,
   mCurrentCookedStat.cookedPointerData.pointerCoords,
   mCurrentCookedStat.cookedPointerData.idToIndex,

......

TouchInputMapper::sync() +-> syncTouch(when, next); --> 數據從slot到outState->rawPointerData.pointers[outCount];

                     +-> processRawTouches() --> cookAndDispatch() --> dispatchTouches() --> dispatchMotion()

數據處理完後將 NotifyMotionArgs 壓入mArgsQueue
TouchInputMapper::dispatchMotion() --> getListener()->notifyMotion(&args) -->
frameworks/native/services/inputflinger/InputListener.cpp
void NotifyMotionArgs::notify(const sp<InputListenerInterface>& listener) const {

listener->notifyMotion(this);

}
void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {

mArgsQueue.push(new NotifyMotionArgs(*args));

}

void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {

size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();

......

for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
    const MultiTouchMotionAccumulator::Slot* inSlot =
            mMultiTouchMotionAccumulator.getSlot(inIndex);

......

    RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
    outPointer.x = inSlot->getX();
    outPointer.y = inSlot->getY();

......
}

數據從MultiTouchMotionAccumulator::Slot 轉到 RawPointerData::Pointer

void TouchInputMapper::processRawTouches(bool timeout) {
....//在處理mRawStatesPending數據時,一個一個取出給mCurrentRawState,然後 cookAndDispatch進一步處理
    for(count = 0; count < N; count++) {
        const RawState& next = mRawStatesPending[count];
......//給mCurrentRawState
        mCurrentRawState.copyFrom(next);
......//cookAndDispatch加工並分發
        cookAndDispatch(mCurrentRawState.when);

void TouchInputMapper::cookPointerData() {
    uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount;

    mCurrentCookedState.cookedPointerData.clear();
......//將數據進一步的處理,例如,計算旋轉後的值
    // Walk through the the active pointers and map device coordinates onto
    // surface coordinates and adjust for display orientation.
    for (uint32_t i = 0; i < currentPointerCount; i++) {
        const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];
......
        case DISPLAY_ORIENTATION_90:
            x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
            y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate;
......//將值給 cookedPointerData.pointerCoords
        // Write output coords.
        PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i];
        out.clear();
        out.setAxisValue(AMOTION_EVENT_AXIS_X, x);
        out.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
        out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
......//將值給 cookedPointerData.pointerProperties
        // Write output properties.
        PointerProperties& properties =
                mCurrentCookedState.cookedPointerData.pointerProperties[i];
        uint32_t id = in.id;
        properties.clear();
        properties.id = id;
        properties.toolType = in.toolType;

        // Write id index.
        mCurrentCookedState.cookedPointerData.idToIndex[id] = i;
......
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章