Learn android input system from simple to deep - input event collection (InputReader)

Posted by jarvishr on Fri, 28 Jan 2022 02:28:36 +0100

summary

As mentioned earlier, the system process distributes events to the app process through InputDispatcher.

Previous address: From simple to deep learning android input system (III) - InputChannel analysis

So where did the InputDispatcher incident come from?
How does android receive events from the hardware layer?
This paper will explore this.

InputReader

The answer itself is that in the InputReader class, InputReader will start a thread to cycle through the logic, mainly doing two things:

  1. Get events for hardware devices
  2. Process the obtained raw data and pass it to InputDispatcher
void InputReader::loopOnce() {
--------ellipsis
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
--------ellipsis
        if (count) {
            processEventsLocked(mEventBuffer, count);
        }
--------ellipsis
}

InputReader get hardware events

InputReader obtains hardware events through the getEvents() method of mEventHub.

Meeventhub listens to the device nodes in the / dev/input directory. Interested readers can see the meeventhub source code by themselves.

The code logic is as follows:

  1. Start a for loop to get the event. If there is no event, it will be locked. The logic will not continue to execute until an event comes in.
  2. If a device has been shut down, you need to delete it from the listening device and record "device shutdown" as an event in event.
  3. If a new device is added, you need to add the linked list of listening devices and record "device addition" as an event.
  4. Traverse the hardware events, first find the hardware corresponding to the event, and then read the data related to the event from the hardware and store it in event.
  5. Finally, the number of events is returned. (there is a reference to the event object outside the method itself, so there is no need to pass out the event.)
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    ALOG_ASSERT(bufferSize >= 1);

    AutoMutex _l(mLock);

    struct input_event readBuffer[bufferSize];

    RawEvent* event = buffer;
    size_t capacity = bufferSize;
    bool awoken = false;
    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;
            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->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;
        while (mPendingEventIndex < mPendingEventCount) {
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            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;
            }

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

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

            Device* device = mDevices.valueAt(deviceIndex);
            if (eventItem.events & EPOLLIN) {
                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) {
                    if (errno != EAGAIN && errno != EINTR) {
                        ALOGW("could not get event (errno=%d)", errno);
                    }
                } else if ((readSize % sizeof(struct input_event)) != 0) {
                    ALOGE("could not get event (wrong size: %d)", readSize);
                } else {
                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;

                    size_t count = size_t(readSize) / sizeof(struct input_event);
                    for (size_t i = 0; i < count; i++) {
                        struct input_event& iev = readBuffer[i];
                        ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d",
                                device->path.string(),
                                (int) iev.time.tv_sec, (int) iev.time.tv_usec,
                                iev.type, iev.code, iev.value);


                        if (iev.type == EV_MSC) {
                            if (iev.code == MSC_ANDROID_TIME_SEC) {
                                device->timestampOverrideSec = iev.value;
                                continue;
                            } else if (iev.code == MSC_ANDROID_TIME_USEC) {
                                device->timestampOverrideUsec = iev.value;
                                continue;
                            }
                        }
                        if (device->timestampOverrideSec || device->timestampOverrideUsec) {
                            iev.time.tv_sec = device->timestampOverrideSec;
                            iev.time.tv_usec = device->timestampOverrideUsec;
                            if (iev.type == EV_SYN && iev.code == SYN_REPORT) {
                                device->timestampOverrideSec = 0;
                                device->timestampOverrideUsec = 0;
                            }
                            ALOGV("applied override time %d.%06d",
                                    int(iev.time.tv_sec), int(iev.time.tv_usec));
                        }


                        event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
                                + nsecs_t(iev.time.tv_usec) * 1000LL;
                        ALOGV("event time %" PRId64 ", now %" PRId64, event->when, now);

                        if (event->when >= now + 10 * 1000000000LL) {
                            // Double-check.  Time may have moved on.
                            nsecs_t time = systemTime(SYSTEM_TIME_MONOTONIC);
                            if (event->when > time) {
                                ALOGW("An input event from %s has a timestamp that appears to "
                                        "have been generated using the wrong clock source "
                                        "(expected CLOCK_MONOTONIC): "
                                        "event time %" PRId64 ", current time %" PRId64
                                        ", call time %" PRId64 ".  "
                                        "Using current time instead.",
                                        device->path.string(), event->when, time, now);
                                event->when = time;
                            } else {
                                ALOGV("Event time is ok but failed the fast path and required "
                                        "an extra call to systemTime: "
                                        "event time %" PRId64 ", current time %" PRId64
                                        ", call time %" PRId64 ".",
                                        event->when, time, now);
                            }
                        }
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        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.
                        mPendingEventIndex -= 1;
                        break;
                    }
                }
            } else if (eventItem.events & EPOLLHUP) {
                ALOGI("Removing device %s due to epoll hang-up event.",
                        device->identifier.name.string());
                deviceChanged = true;
                closeDeviceLocked(device);
            } else {
                ALOGW("Received unexpected epoll event 0x%08x for device %s.",
                        eventItem.events, device->identifier.name.string());
            }
        }
        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
            mPendingINotify = false;
            readNotifyLocked();
            deviceChanged = true;
        }

        // Report added or removed devices immediately.
        if (deviceChanged) {
            continue;
        }

        // Return now if we have collected any events or if we were explicitly awoken.
        if (event != buffer || awoken) {
            break;
        }
        mPendingEventIndex = 0;

        mLock.unlock(); // release lock before poll, must be before release_wake_lock
        release_wake_lock(WAKE_LOCK_ID);

        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
        mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock

        if (pollResult == 0) {
            // Timed out.
            mPendingEventCount = 0;
            break;
        }

        if (pollResult < 0) {
            mPendingEventCount = 0;
            if (errno != EINTR) {
                ALOGW("poll failed (errno=%d)\n", errno);
                usleep(100000);
            }
        } else {
            // Some events occurred.
            mPendingEventCount = size_t(pollResult);
        }
    }

    return event - buffer;
}

InputReader handles hardware events

There are many kinds of hardware events, including "device input", "device add", "device delete", etc.
”The device input also includes "keyboard event" and "touch event".
In this paper, we mainly explore the "touch event" in "device input".

The called task stack is as follows:

  • InputReader.loopOnce()
  • InputReader.processEventsLocked()
  • InputReader.processEventsForDeviceLocked()
  • InputDevice.process()
  • InputMapper.process()
    Equivalent to touchinputmapper process()
  • TouchInputMapper.sync()
  • TouchInputMapper.processRawTouches()
  • TouchInputMapper.cookAndDispatch()
  • TouchInputMapper.dispatchTouches()
  • TouchInputMapper.dispatchMotion()
  • getListener().notifyMotion()
    Equivalent to inputdispatcher notifyMotion()
  • InputDispatcher.dispatchOnce()

The logic is roughly as follows:

  1. After receiving the event, InputReader hands over the original data to InputDevice for processing.
  2. Each InputDevice will have its own Mapper for handling events, and InputDevice will hand over the events to InputDevice for further processing. What is explored here is the touch event, so it is TouchInputMapper.
    (InputDevice also includes SwitchInputMapper,KeyBoardInputMapper, etc., which correspond to different input methods respectively)
  3. TouchInputMapper will convert the original data into the data that can be received by InputDispatcher, transfer the data to InputDispatcher, and then wake up the InputDispatcher thread. Finally, it will be processed in dispatchOnce() of InputDispatcher.

Readers who are not familiar with InputDispatcher can read the previous articles of the writer:

Continue to explore

Question:

  1. When does InputReader initialize?
  2. When does InputDispatcher initialize?

Interested readers can pay attention to the author's subsequent articles, or read the relevant source code by themselves.

Topics: Java Android input