主頁 > 移動端開發 > Android 11.0原始碼系列之IMS(五)InputReader

Android 11.0原始碼系列之IMS(五)InputReader

2021-06-14 07:24:12 移動端開發

本篇涉及到的主要代碼:

frameworks/native/services/inputflinger/

上一篇我們介紹了InputChannel以及InputConnection的創建以及它們在事件分發流程中的作用,至此輸入事件從native層的InputDispatcher傳遞到了位于應用行程空間的ViewRootImpl,但是InputDispatcher的輸入事件又從何而來呢?本篇將會為大家解答第三篇中留下的這個疑問,

1.1 InputReader的創建

[-> InputManager.cpp]

InputManager::InputManager(
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = createInputDispatcher(dispatcherPolicy);
    mClassifier = new InputClassifier(mDispatcher);
    // 將NativeInputManager作為InputReaderPolicyInterface傳遞給InputReader
    // 將InputClassfier作為InputListenerInterface傳遞給InputReader
    mReader = createInputReader(readerPolicy, mClassifier);
}

[-> InputReaderFactory.cpp]

sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy,
                                           const sp<InputListenerInterface>& listener) {
    // 創建InputReader[見1.2小節
    // 創建EventHub[見1.5小節]
    return new InputReader(std::make_unique<EventHub>(), policy, listener);
}

回顧一下第二篇提到的InputDispatcher和InputReader的創建程序:NativeInputManager繼承于InputReaderPolicyInterface和InputDispatcherPolicyInterface,它會被傳遞為InputReader建構式的第二個引數;InputClassfier繼承于InputClassifierInterface,而InputClassifierInterface又繼承于InputListenerInterface,所以它會被傳遞為InputReader建構式的第三個引數;

1.2 InputReader建構式

[-> InputReader.cpp]

// --- InputReader ---
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
                         const sp<InputReaderPolicyInterface>& policy,
                         const sp<InputListenerInterface>& listener)
      : mContext(this),
        // 初始化EventHub
        mEventHub(eventHub),
        // 初始化mPolicy
        mPolicy(policy),
        mGlobalMetaState(0),
        mGeneration(1),
        mNextInputDeviceId(END_RESERVED_ID),
        mDisableVirtualKeysTimeout(LLONG_MIN),
        mNextTimeout(LLONG_MAX),
        mConfigurationChangesToRefresh(0) {
    // 創建QueuedInputListener  
    mQueuedListener = new QueuedInputListener(listener);

    { // acquire lock
        AutoMutex _l(mLock);
        // 通過mPolicy即NativeManager呼叫JNI方法getReaderConfiguration獲取配置,
        // 而它最侄訓調到Java層IMS的一些方法獲取保存在檔案中的一些系統配置項
        refreshConfigurationLocked(0);
        updateGlobalMetaStateLocked();
    } // release lock
}

[-> InputListener.cpp]

QueuedInputListener::QueuedInputListener(const sp<InputListenerInterface>& innerListener) :
        // 初始化mInnerListener
        mInnerListener(innerListener) {
}

InputReader建構式中,創建了一個非常重要的物件mQueuedListener,它將在InputReader執行緒啟動后將讀取到的輸入事件傳遞給InputDispatcher;

1.3 InputReader執行緒啟動

[-> InputReader.cpp]

status_t InputReader::start() {
    // 只能啟動一次
    if (mThread) {
        return ALREADY_EXISTS;
    }
    mEventHub->wake
    // 創建名為"InputReader"的執行緒,其loop方法和wake方法分別為loopOnce()[見1.4小節]和mEventHub->wake()
    mThread = std::make_unique<InputThread>(
            "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
    return OK;
}

在第三篇中我們分析過"InputThread只是對InputThreadImpl的封裝,InputThreadImpl才是一個真正的執行緒",同InputDispatcher一樣:在呼叫InputThreadImpl.run方法之后,執行緒就開始運行起來并呼叫threadLoop方法開始訊息佇列的回圈處理,這樣也就回到了InputReader.loopOnce()方法;

1.4 訊息回圈loopOnce()

[-> InputReader.cpp]

void InputReader::loopOnce() {
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    std::vector<InputDeviceInfo> inputDevices;
    // 通過EventHub獲取輸入事件[見1.6小節]
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();

        if (count) {
            // 處理輸入事件[見1.7小節]
            processEventsLocked(mEventBuffer, count);
        }
    } // release lock
    ......
    // Flush queued events out to the listener.
    // This must happen outside of the lock because the listener could potentially call
    // back into the InputReader's methods, such as getScanCodeState, or become blocked
    // on another thread similarly waiting to acquire the InputReader lock thereby
    // resulting in a deadlock.  This situation is actually quite plausible because the
    // listener is actually the input dispatcher, which calls into the window manager,
    // which occasionally calls into the input reader.
    // 將輸入事件傳遞給InputDispatcher[見1.13小節]
    mQueuedListener->flush();
}

loopOnce()方法通過EventHub.getEvents源源不斷的獲取RawEvent事件,然后交由processEventsLocked作預處理,最后通過QueuedListener->flush方法將輸入事件傳遞給InputDispatcher,在這之前我們首先要看看EventHub是什么,它在建構式中又做了些什么;

1.5 EventHub建構式

[-> EventHub.cpp]

EventHub::EventHub(void)
      : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
        mNextDeviceId(1),
        mControllerNumbers(),
        mOpeningDevices(nullptr),
        mClosingDevices(nullptr),
        mNeedToSendFinishedDeviceScan(false),
        mNeedToReopenDevices(false),
        mNeedToScanDevices(true),
        mPendingEventCount(0),
        mPendingEventIndex(0),
        mPendingINotify(false) {
    ensureProcessCanBlockSuspend();

    // 創建epoll并保存生成的檔案描述符mEpollFd
    mEpollFd = epoll_create1(EPOLL_CLOEXEC);
    // 創建inotify并保存生成的檔案描述符mINotifyFd
    mINotifyFd = inotify_init();
    // 此處DEVICE_PATH為"/dev/input",利用mINotifyFd監聽該設備結點
    mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);

    struct epoll_event eventItem = {};
    eventItem.events = EPOLLIN | EPOLLWAKEUP;
    eventItem.data.fd = mINotifyFd;
    //添加iNotify到epoll實體
    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);

    int wakeFds[2];
    // 創建管道
    result = pipe(wakeFds);
    // 讀管道
    mWakeReadPipeFd = wakeFds[0];
    // 寫管道
    mWakeWritePipeFd = wakeFds[1];
    //將pipe的讀和寫都設定為非阻塞方式
    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
    //添加管道的讀端到epoll實體
    eventItem.data.fd = mWakeReadPipeFd;
    result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
}

EventHub在建構式中主要完成了以下幾項作業:創建epoll實體;初始化iNotify監聽/dev/input設備結點,并添加到epoll實體;創建非阻塞模式的管道,并將管道的讀端添加到epoll;

1.6 EventHub.getEvents

[-> EventHub.cpp]

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);

    AutoMutex _l(mLock);
    // 讀取到的input_event
    struct input_event readBuffer[bufferSize];
    // 原始事件,容量大小為256
    RawEvent* event = buffer;
    size_t capacity = bufferSize;
    bool awoken = false;
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
        ......
        // 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.c_str());
            mClosingDevices = device->next;
            event->when = now;
            event->deviceId = (device->id == mBuiltInKeyboardId)
                    ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID
                    : device->id;
            // 移除設備的事件
            event->type = DEVICE_REMOVED;
            event += 1;
            delete device;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }
        ......
        while (mOpeningDevices != nullptr) {
            Device* device = mOpeningDevices;
            ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str());
            mOpeningDevices = device->next;
            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;
            }
        }

        // Grab the next input event.
        bool deviceChanged = false;
        // mPendingEventIndex和mPendingEventCount分別代表待處理事件的索引和待處理事件的個數
        // 當通過epoll讀取到epoll_event事件之后就會回圈處理所有的mPendingEventItems
        while (mPendingEventIndex < mPendingEventCount) {
            // 根據索引取出一個epoll_event事件
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            ......
            // 根據epoll_data.fd獲取對應的設備
            Device* device = getDeviceByFdLocked(eventItem.data.fd);
            ......
            // This must be an input event
            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.
                    ALOGW("could not get event, removed? (fd: %d size: %" PRId32
                          " bufferSize: %zu capacity: %zu errno: %d)\n",
                          device->fd, readSize, bufferSize, capacity, errno);
                    deviceChanged = true;
                    // 設備已被移除則執行關閉操作
                    closeDeviceLocked(device);
                } else if (readSize < 0) {
                    ......
                } else if ((readSize % sizeof(struct input_event)) != 0) {
                    ......
                } else {
                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;

                    size_t count = size_t(readSize) / sizeof(struct input_event);
                    // 將讀取到的所有input_event轉換成RawEvent
                    for (size_t i = 0; i < count; i++) {
                        struct input_event& iev = readBuffer[i];
                        event->when = processEventTimestamp(iev);
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event += 1;
                        capacity -= 1;
                    }
                    // 傳入的buffer已經填滿,將mPendingEventIndex索引減一后退出while回圈
                    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.
                        mPendingEventIndex -= 1;
                        break;
                    }
                }
            }
        }
        ......
        // Poll for events.
        // When a device driver has pending (unread) events, it acquires
        // a kernel wake lock.  Once the last pending event has been read, the device
        // driver will release the kernel wake lock, but the epoll will hold the wakelock,
        // since we are using EPOLLWAKEUP. The wakelock is released by the epoll when epoll_wait
        // is called again for the same fd that produced the event.
        // Thus the system can only sleep if there are no events pending or
        // currently being processed.
        //
        // The timeout is advisory only.  If the device is asleep, it will not wake just to
        // service the timeout.
        // 重置輸入事件的索引為0
        mPendingEventIndex = 0;

        mLock.unlock(); // release lock before poll

        // 等待input事件的到來,并將獲取到的epoll_event事件填充到mPendingEventItems
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

        mLock.lock(); // reacquire lock after poll

        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);
                // 系統出現錯誤則休眠1s
                usleep(100000);
            }
        } else {
            // Some events occurred.
            // epoll_wait的回傳值為input事件的個數
            mPendingEventCount = size_t(pollResult);
        }
    }

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

EventHub采用iNotify + epoll機制實作監聽目錄/dev/input下的設備節點,在getEvents方法中將input_event結構體 + deviceId 轉換成RawEvent結構體;

1.7 InputReader.processEventsLocked

[-> InputReader.cpp]

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    // 回圈處理每一個RawEvent
    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;
            }
            // 真正處理RawEvent的邏輯[見1.8小節]
            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;
    }
}

這里會根據RawEvent的type來分別處理:如果是真正的input輸入事件則單獨處理,否則再繼續細分處理方案(包括輸入設備的添加,洗掉和掃描不是本篇的重點,這里不作討論);

1.8 InputReader.processEventsForDeviceLocked

[-> InputReader.cpp]

void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
                                               size_t count) {
    auto deviceIt = mDevices.find(eventHubId);
    // 沒找到輸入設備
    if (deviceIt == mDevices.end()) {
        ALOGW("Discarding event for unknown eventHubId %d.", eventHubId);
        return;
    }

    std::shared_ptr<InputDevice>& device = deviceIt->second;
    if (device->isIgnored()) {
        // ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }
    // 直接呼叫輸入設備的proesss方法[見1.9小節]
    device->process(rawEvents, count);
}
1.9 InputDevice.process

[-> InputDevice.cpp]

void InputDevice::process(const RawEvent* rawEvents, size_t count) {
    // Process all of the events in order for each mapper.
    // We cannot simply ask each mapper to process them in bulk because mappers may
    // have side-effects that must be interleaved.  For example, joystick movement events and
    // gamepad button presses are handled by different mappers but they should be dispatched
    // in the order received.
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
        if (mDropUntilNextSync) {
            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                mDropUntilNextSync = false;
            }
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
            mDropUntilNextSync = true;
            reset(rawEvent->when);
        } else {
            // 根據RawEvent的deviceId查找對應的InputMapper,并繼續執行其process方法[1.10方法]
            for_each_mapper_in_subdevice(rawEvent->deviceId, [rawEvent](InputMapper& mapper) {
                mapper.process(rawEvent);
            });
        }
        --count;
    }
}

// run a function against every mapper on a specific subdevice
inline void for_each_mapper_in_subdevice(int32_t eventHubDevice,
                                         std::function<void(InputMapper&)> f) {
    // mDevices是一個map:key為deviceId,value為DevicePair
    auto deviceIt = mDevices.find(eventHubDevice);
    if (deviceIt != mDevices.end()) {
        // mDevices的second是一個DevicePair
        auto& devicePair = deviceIt->second;
        // DevicePair的second是一個InputMapper
        auto& mappers = devicePair.second;
        for (auto& mapperPtr : mappers) {
            f(*mapperPtr);
        }
    }
}

在添加輸入設備的程序中,InputReader會根據輸入設備的class型別添加不同的InputMapper,一種或者多種class可能對應一個InputMapper,比如代表按鍵事件的KeyboardInputMapper,代表單點觸摸的SingleTouchInputMapper和代表多點觸摸的MultiTouchInputMapper,它們都是InputMapper的子類:

void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) {
    // 已經添加過的就不再重復添加
    if (mDevices.find(eventHubId) != mDevices.end()) {
        return;
    }
    // 初始化InputDeviceContext,this代表InputDevice
    std::unique_ptr<InputDeviceContext> contextPtr(new InputDeviceContext(*this, eventHubId));
    uint32_t classes = contextPtr->getDeviceClasses();
    std::vector<std::unique_ptr<InputMapper>> mappers;
    ......
    // Keyboard-like devices.
    uint32_t keyboardSource = 0;
    int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
    if (classes & INPUT_DEVICE_CLASS_KEYBOARD) {
        keyboardSource |= AINPUT_SOURCE_KEYBOARD;
    }
    if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) {
        keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
    }
    if (classes & INPUT_DEVICE_CLASS_DPAD) {
        keyboardSource |= AINPUT_SOURCE_DPAD;
    }
    if (classes & INPUT_DEVICE_CLASS_GAMEPAD) {
        keyboardSource |= AINPUT_SOURCE_GAMEPAD;
    }
    // 根據class獲取不同的keyboardSource
    if (keyboardSource != 0) {
        mappers.push_back(
                std::make_unique<KeyboardInputMapper>(*contextPtr, keyboardSource, keyboardType));
    }

    // Touchscreens and touchpad devices.
    if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) {
        // 多點觸摸則添加MultiTouchInputMapper
        mappers.push_back(std::make_unique<MultiTouchInputMapper>(*contextPtr));
    } else if (classes & INPUT_DEVICE_CLASS_TOUCH) {
        // 單點觸摸則添加SingleTouchInputMapper
        mappers.push_back(std::make_unique<SingleTouchInputMapper>(*contextPtr));
    }
    ......
    // insert the context into the devices set
    mDevices.insert({eventHubId, std::make_pair(std::move(contextPtr), std::move(mappers))});
}

接下來我們將以KeyboardInputMapper為例來展開說明;

1.10 KeyboardInputMapper.process

[-> KeyboardInputMapper.cpp]

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)) {
                // procesKey[見1.11小節]
                processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
            }
            break;
        }
        case EV_MSC: {
            ......
        }
        case EV_SYN: {
            ......
        }
    }
}
1.11 KeyboardInputMapper.processKey

[-> KeyboardInputMapper.cpp]

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

    // 根據scanCode找到對應的keyCode
    if (getDeviceContext().mapKey(scanCode, usageCode, mMetaState, &keyCode, &keyMetaState,
                                  &policyFlags)) {
        keyCode = AKEYCODE_UNKNOWN;
        keyMetaState = mMetaState;
        policyFlags = 0;
    }

    if (down) {
        // Rotate key codes according to orientation if needed.
        if (mParameters.orientationAware) {
            keyCode = rotateKeyCode(keyCode, getOrientation());
        }

        // Add key down.
        ssize_t keyDownIndex = findKeyDown(scanCode);
        if (keyDownIndex >= 0) {
            // key repeat, be sure to use same keycode as before in case of rotation
            // mKeyDowns記錄著所有按下的鍵
            keyCode = mKeyDowns[keyDownIndex].keyCode;
        } else {
            // key down
            if ((policyFlags & POLICY_FLAG_VIRTUAL) &&
                getContext()->shouldDropVirtualKey(when, keyCode, scanCode)) {
                return;
            }
            if (policyFlags & POLICY_FLAG_GESTURE) {
                getDeviceContext().cancelTouch(when);
            }

            KeyDown keyDown;
            keyDown.keyCode = keyCode;
            keyDown.scanCode = scanCode;
            // 將此次down事件壓入堆疊頂
            mKeyDowns.push_back(keyDown);
        }
        // 同時記錄down事件時間
        mDownTime = when;
    } else {
        // Remove key down.
        ssize_t keyDownIndex = findKeyDown(scanCode);
        if (keyDownIndex >= 0) {
            // key up, be sure to use same keycode as before in case of rotation
            keyCode = mKeyDowns[keyDownIndex].keyCode;
            // 按鍵抬起,則移除down事件
            mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex);
        } else {
            // key was not actually down
            ALOGI("Dropping key up from device %s because the key was not down.  "
                  "keyCode=%d, scanCode=%d",
                  getDeviceName().c_str(), keyCode, scanCode);
            return;
        }
    }

    nsecs_t downTime = mDownTime;
    // 創建NotifyKeyArgs物件, when記錄eventTime, downTime記錄按下時間;
    NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, getDisplayId(),
                       policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
                       AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
    // 通過getListener->notifyKey將事件通知出去,這里的getListener指誰呢?[見1.12小節]
    getListener()->notifyKey(&args);
}

[-> InputMapper.h]

// KeyboardInputMapper呼叫父類InputMapper.getListener
inline InputListenerInterface* getListener() { return getContext()->getListener(); }
// InputMapper.getContext又呼叫了mDeviceContext.getContext()
inline InputReaderContext* getContext() { return mDeviceContext.getContext(); }

// mDeviceContext是在InputMapper建構式中通過傳入的InputDeviceContext初始化的,在1.9節中的addEventHubDevice中可以看
// 到這里的deviceContext是指InputDevice,繼續呼叫InputDevice.getContext()
InputMapper::InputMapper(InputDeviceContext& deviceContext) : mDeviceContext(deviceContext) {}
// InputDevice的mContext也是在建構式中傳入的
inline InputReaderContext* getContext() { return mContext; }

[-> InputDevice.cpp]

InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
                         const InputDeviceIdentifier& identifier)
      : mContext(context),
        mId(id),
        mGeneration(generation),
        mControllerNumber(0),
        mIdentifier(identifier),
        mClasses(0),
        mSources(0),
        mIsExternal(false),
        mHasMic(false),
        mDropUntilNextSync(false) {}

[-> InputReader.cpp]

std::shared_ptr<InputDevice> InputReader::createDeviceLocked(
        int32_t eventHubId, const InputDeviceIdentifier& identifier) {
    auto deviceIt = std::find_if(mDevices.begin(), mDevices.end(), [identifier](auto& devicePair) {
        return devicePair.second->getDescriptor().size() && identifier.descriptor.size() &&
                devicePair.second->getDescriptor() == identifier.descriptor;
    });

    std::shared_ptr<InputDevice> device;
    if (deviceIt != mDevices.end()) {
        device = deviceIt->second;
    } else {
        int32_t deviceId = (eventHubId < END_RESERVED_ID) ? eventHubId : nextInputDeviceIdLocked();
        // InputDevice在createDeviceLocked中創建,其mContext也是在InputReader的建構式中賦值的即InputReader
        device = std::make_shared<InputDevice>(&mContext, deviceId, bumpGenerationLocked(),
                                               identifier);
    }
    device->addEventHubDevice(eventHubId);
    return device;

}

// 最終getListener()指向的還是QueuedListener
InputListenerInterface* InputReader::ContextImpl::getListener() {
    return mReader->mQueuedListener.get();
}

通過一級級的往上回溯,最終我們找到了getListener()回傳值即在InputReader建構式中創建的QueuedListener;

1.12 QueuedListener.notifyKey

[-> InputListener.cpp]

std::vector<NotifyArgs*> mArgsQueue;

void QueuedInputListener::notifyKey(const NotifyKeyArgs* args) {
    traceEvent(__func__, args->id);
    mArgsQueue.push_back(new NotifyKeyArgs(*args));
}

void QueuedInputListener::notifyMotion(const NotifyMotionArgs* args) {
    traceEvent(__func__, args->id);
    mArgsQueue.push_back(new NotifyMotionArgs(*args));
}

不管是NotifyKeyArgs(按鍵事件)還是NotifyMotionArgs(觸摸事件),它們都繼承于NotifyArgs,這里都會把它們壓堆疊到mArgsQueue中,等待下一步的處理;

1.13 QueuedListener.flush()

[-> InputListener.cpp]

void QueuedInputListener::flush() {
    size_t count = mArgsQueue.size();
    for (size_t i = 0; i < count; i++) {
        NotifyArgs* args = mArgsQueue[i];
        // 依次從mArgsQueue中取出每一個NotifyArgs,并執行其notify方法[見1.14小節]
        args->notify(mInnerListener);
        delete args;
    }
    mArgsQueue.clear();
}

// notify方法又呼叫了mInnerListener.notifyKey[見1.14小節]
void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const {
    listener->notifyKey(this);
}

根據1.1和1.2小節的分析,mInnerListener就是傳入InputReader的InputClassifier;

1.14 InputClassifier.notifyKey

[-> InputClassifier.cpp]

void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
    // pass through
    // 直接呼叫mListener->notifyKey[見1.15小節]
    mListener->notifyKey(args);
}

同樣根據前幾篇文章的分析,這里的mListener就是傳入InputClassfier建構式中的InputDispatcher;

1.15 InputDispatcher.notifyKey

[-> InputDispatcher.cpp]

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
    // 按鍵事件只支持KEY_DOWN和KEY_UP
    if (!validateKeyEvent(args->action)) {
        return;
    }
    ......
    KeyEvent event;
    // 將NotifyKeyArgs轉換為KeyEvent
    event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC,
                     args->action, flags, keyCode, args->scanCode, metaState, repeatCount,
                     args->downTime, args->eventTime);
    
    android::base::Timer t;
    // 由NativeInputManager經IMS->WMS->InputManagerCallback將按鍵訊息交由PhoneWindowManager確認是否提前處理
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
    // 處理時間是否超過50ms
    if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
        ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms",
              std::to_string(t.duration().count()).c_str());
    }
    
    bool needWake;
    { // acquire lock
        mLock.lock();
        // 是否需要攔截輸入事件
        if (shouldSendKeyToInputFilterLocked(args)) {
            mLock.unlock();
    
            policyFlags |= POLICY_FLAG_FILTERED;
            if (!mPolicy->filterInputEvent(&event, policyFlags)) {
                // 輸入事件被InputFilter消費就不再往下傳遞
                return; // event was consumed by the filter
            }
    
            mLock.lock();
        }
        // 構造KeyEntry
        KeyEntry* newEntry =
                new KeyEntry(args->id, args->eventTime, args->deviceId, args->source,
                             args->displayId, policyFlags, args->action, flags, keyCode,
                             args->scanCode, metaState, repeatCount, args->downTime);
    
        // 將KeyEntry添加到mInboundQueue佇列
        needWake = enqueueInboundEventLocked(newEntry);
        mLock.unlock();
    } // release lock
    
    if (needWake) {
        // 喚醒InputDispatcher的執行緒
        mLooper->wake();
    }
}

首先將NotifyKeyArgs轉換成KeyEvent,然后交由mPolicy(在第一篇文章中我們分析過這里的mPolicy是經由JNI層的NativeManager指向Java層的IMS)分別經過interceptKeyBeforeQueueing和filterInputEvent的處理;如果事件不被攔截的話最侄訓入隊到mInboundQueue中,這里的mInboundQueue正是我們在第三篇中分析的InputDispatcher處理輸入事件的來源:有了它InputDispatcher就可以不斷的從中取出事件并通過InputChannel的socket發送給應用端去處理了,

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/287177.html

標籤:其他

上一篇:2020年9月,面對“金九銀十”,一個無法跳槽的程式員附答案

下一篇:Android 用字串資源名稱獲取int型資源id的幾種方式

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more