[Android] Input event distribution process InputReader(2)

After the analysis of the IMS construction method is completed, let’s take a look at the start method in IMS

public void start() {<!-- -->
        Slog.i(TAG, "Starting input manager");
        // InputManager->inputDispatcher & amp; & amp;inputReader was initialized before
        //Start them here and create InputThread threads, namely InputReaderThread and InputDispatcherThread
        // InputThread is a custom class that accepts three parameters: a string as the thread name,
        // One lambda expression acts as a thread function, and the other lambda expression is called when the thread ends
        nativeStart(mPtr);

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

        //Register observers for touch point speed and whether to display functions
        registerPointerSpeedSettingObserver();
        registerShowTouchesSettingObserver();
       ...
        //Update the speed of the touch point
        updatePointerSpeedFromSettings();
        // Whether to display touch points on the screen
        updateShowTouchesFromSettings();
        updateAccessibilityLargePointerFromSettings();
        updateDeepPressStatusFromSettings("just booted");
        updateMaximumObscuringOpacityForTouchFromSettings();
        updateBlockUntrustedTouchesModeFromSettings();
    }

The above only focuses on nativeStart(mPtr);, mPtr points to NativeInputManager, InputManager->inputDispatcher & amp; & amp;inputReader was initialized before, start them here, and create InputThread threads, that is, InputReaderThread and InputDispatcherThread

// **com_android_server_input_InputManagerService.cpp**
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {<!-- -->
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    // Get the InputManager and call its start
    status_t result = im->getInputManager()->start();
    if (result) {<!-- -->
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}

// **InputManager.cpp**
status_t InputManager::start() {<!-- -->
    // Call inputDispatcher's start()
    status_t result = mDispatcher->start();
    if (result) {<!-- -->
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }
    //Call start() of inputReader
    result = mReader->start();
    if (result) {<!-- -->
        ALOGE("Could not start InputReader due to error %d.", result);

        mDispatcher->stop();
        return result;
    }

    return OK;
}

InputReader thread

That is, mDispatcher->start(); and mReader->start(); are called in InputManager to create corresponding threads respectively, and let Inputeader and InputDispatcher execute specific logic. Let’s first take a look at the start method in InputReader

status_t InputReader::start() {<!-- -->
    if (mThread) {<!-- -->
        return ALREADY_EXISTS;
    }
    // InputThread is a custom class that accepts three parameters: a string as the thread name, a lambda expression as the thread function,
    // Another lambda expression is called when the thread ends
    mThread = std::make_unique<InputThread>(
            "InputReader", [this]() {<!-- --> loopOnce(); }, [this]() {<!-- --> mEventHub->wake(); });
    return OK;
}

//Here is the main content after start
//Read the device node content in eventHub
//The call is mEventHub->getEvents
void InputReader::loopOnce() {<!-- -->
    int32_t oldGeneration;
    int32_t timeoutMillis;
    bool inputDevicesChanged = false;
    ...
    // Read all events in the device and store them in mEventBuffer
// RawEvent mEventBuffer[EVENT_BUFFER_SIZE] GUARDED_BY(mLock);
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    {<!-- --> // acquire lock
        ...
        if (count) {<!-- -->
            //Key code
            processEventsLocked(mEventBuffer, count);
        }
...
    mQueuedListener->flush();
}

First, the getEvents method in mEventHub is called to obtain the original input events and stored in RawEvent, so the original input events are stored in the entity class. Then the mQueuedListener created by the previous initialization is called, calling flush() sends the event to the InputDispatcher, continue to look at the getEvents method in EventHub

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer/*cache events*/, size_t bufferSize/*256*/) {<!-- -->
    struct input_event readBuffer[bufferSize];

    //original event
    RawEvent* event = buffer;
    // 256
    size_t capacity = bufferSize;
    bool awaken = false;
    //Loop to read device nodes
    for (;;) {<!-- -->
        ...
// std::vector<std::unique_ptr<Device>> mOpeningDevices;
// Initialization actions, mainly opening the device and scanning the device
        // Scan devices and open each device directory /dev/input/...
        scanDevicesLocked();
while (!mOpeningDevices.empty()) {<!-- -->
// Get the files in the device /dev/input/ directory in mOpeningDevices: event1/2/3/4, etc.
            std::unique_ptr<Device> device = std::move(*mOpeningDevices.rbegin());
            mOpeningDevices.pop_back();
            ALOGV("Reporting device opened: id=%d, name=%s\
", device->id, device->path.c_str());
//Package each device to generate RawEvent (event) form to store the corresponding information
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
            //Add device events
            event->type = DEVICE_ADDED;
            event + = 1;
...
        }
//Here is where we start processing events in each device
while (mPendingEventIndex < mPendingEventCount) {<!-- -->
            //Read events from mPendingEventCount
            const struct epoll_event & amp; eventItem = mPendingEventItems[mPendingEventIndex + + ];
            ...
            //Open device files such as /dev/input/... event1/2/3
            Device* device = getDeviceByFdLocked(eventItem.data.fd);
            if (device == nullptr) {<!-- -->
                ALOGE("Received unexpected epoll event 0x x for unknown fd %d.", eventItem.events,
                      eventItem.data.fd);
                ALOG_ASSERT(!DEBUG);
                continue;
            }
...
            // This must be an input event
            //Read input events from the device
            if (eventItem.events & amp; EPOLLIN) {<!-- -->
                //Read the event content in the device
                int32_t readSize =
                        read(device->fd, readBuffer, sizeof(struct input_event) * capacity);
                if (readSize == 0 || (readSize < 0 & amp; & errno == ENODEV)) {<!-- -->
                    // Device was removed before INotify noticed.
                    ALOGW("could not get event, removed? (fd: %d size: %" PRId32
                          " bufferSize: %zu capacity: %zu errno: %d)\
",
                          device->fd, readSize, bufferSize, capacity, errno);
                    deviceChanged = true;
// If there is no event, close the device
                    closeDeviceLocked(*device);
                } ... else {<!-- -->
                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;

                    // Get the number of device input events
                    size_t count = size_t(readSize) / sizeof(struct input_event);
                    for (size_t i = 0; i < count; i + + ) {<!-- -->
                        //Package each event to generate RawEvent
                        struct input_event & amp; iev = readBuffer[i];
                        event->when = processEventTimestamp(iev);
                        event->readTime = systemTime(SYSTEM_TIME_MONOTONIC);
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->code = iev.code;
                        event->value = iev.value;
                        event + = 1;
                        capacity -= 1;
                    }
                    ...
                }
            }
...
        }
...
// Release the lock before polling
        mLock.unlock(); // release lock before poll
        // Wait for input event to arrive
        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
        // Request the lock when the event arrives
        mLock.lock(); // reacquire lock after poll
...

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

The above content is that scanDevicesLocked performs the initialization action, scans the device, opens the device, saves the device to mOpeningDevices, then loops to read the events in each device, encapsulates each event into a RawEvent and adds DEVICE_ADDED This flag (will be used later to decide whether to remove or add), and then save it to buffer (mEventBuffer). If there are no events in the device, it will Turn off the device, and then wait for the next input event to arrive through epoll_wait, so we return to InputReader.cpp#loopOnce to continue analyzing processEventsLocked(mEventBuffer, count);

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {<!-- -->
    // rawEvents saves the events in the device obtained through EventHub
    // count is the number of events
    for (const RawEvent* rawEvent = rawEvents; count;) {<!-- -->
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {<!-- -->
            ...
//Event handling
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {<!-- -->
            // Processed in EventHub.getEvents, each event will be encapsulated as RawEvent, and the DEVICE_ADDED flag will be added
            switch (rawEvent->type) {<!-- -->
                case EventHubInterface::DEVICE_ADDED:
                    // Create a device, eventHubId is the unique id of the device, and the device can be obtained through the id
//The main purpose here is to create an InputDevice and determine whether it is a mouse type touch event, keyboard type, or touch screen type based on the device.
                    addDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::DEVICE_REMOVED:
                    removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
                    break;
                case EventHubInterface::FINISHED_DEVICE_SCAN:
                    handleConfigurationChangedLocked(rawEvent->when);
                    break;
                default:
                    ALOG_ASSERT(false); // can't happen
                    break;
            }
        }
        // After processing the event, the count is reduced
        count -= batchSize;
        //After processing the event, the pointer moves backward one
        rawEvent + = batchSize;
    }
}

void InputReader::processEventsForDeviceLocked(int32_t eventHubId, const RawEvent* rawEvents,
                                               size_t count) {<!-- -->
    auto deviceIt = mDevices.find(eventHubId);
    ...
    //Call the process of InputDevice
    // Pass the original event + the number of events
    device->process(rawEvents, count);
}

First, addDeviceLocked will be used to create an InputDevice, because the functions of each device in /dev/input are different. Some handle fingerprint events, some handle mouse and keyboard data, and some handle screen touch events, and then execute to processEventsForDeviceLocked, because processEventsForDeviceLocked has created various types of InputDevice before execution, here is to call the InputDevice.process method of the corresponding type, then just find a TouchInputMapper.cppLet’s analyze, touch events

frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp

void TouchInputMapper::process(const RawEvent* rawEvent/*raw event*/) {<!-- -->
//Mouse press, scroll, touch
    mCursorButtonAccumulator.process(rawEvent);
    mCursorScrollAccumulator.process(rawEvent);
    mTouchButtonAccumulator.process(rawEvent);

    if (rawEvent->type == EV_SYN & amp; & amp; rawEvent->code == SYN_REPORT) {<!-- -->
        // Mainly focus on sync
        sync(rawEvent->when, rawEvent->readTime);
    }
}

sync will continue to call processRawTouches, and then look at

 // It processes raw touch input data and converts it into readable touch events.
// First, it checks if the device mode is DISABLED. If so, it cancels the current touch, clears the current state (mCurrentCookedState), and then updates the touch point.
// Next, it handles any pending raw touch state. These states are stored in mRawStatesPending.
// If the current state is a stylus (stylus) state, it will check whether it needs to wait for stylus data.
// If it needs to wait, it will exit the loop. Otherwise, it sets the current state to the next pending state and converts it into a readable touch event via the cookAndDispatch function
void TouchInputMapper::processRawTouches(bool timeout) {<!-- -->
// Check whether the device is in disabled state
    if (mDeviceMode == DeviceMode::DISABLED) {<!-- -->
        // Drop all input if the device is disabled.
        cancelTouch(mCurrentRawState.when, mCurrentRawState.readTime);
        ...
        return;
    }

    // It will handle any pending raw touch states, which are stored in mRawStatesPending.
    const size_t N = mRawStatesPending.size();
    size_t count;
    // Raw event to be processed RawEvent
    for (count = 0; count < N; count + + ) {<!-- -->
        ...
        // Continue to call, mainly to convert the original event into a readable touch event
        cookAndDispatch(mCurrentRawState.when, mCurrentRawState.readTime);
    }
    ...
}

Briefly explain the function of the cookAndDispatch method to determine whether the event type is gesture touch or mouse. If it is a gesture touch, call dispatchPointerGestures for event distribution, and then determine whether the event is moving. , tap presses and other states, performs separate processing and event distribution, and stores the current finger into an integer variable through markBit(id);, so no matter how many fingers touch, all The control form is stored in a variable, and the memory overhead is small. Finally, the dispatchMotion method is called to encapsulate the event status, such as whether it is pressed, the time of pressing, the event action, and the event id, into NotifyMotionArgs , and then distribute it (that is, notify the message and carry the args to the InputDispatcher), actually calling getListener()->notifyMotion( & amp;args);

So continue to look at the notifyMotion method in InputDispatcher.cpp and enter the next stage

Summary:

  1. Starting from the creation of the InputReader thread, the first time is to open the device in /dev/input from the getEvents method in EventHub, then loop to read the events in the device, obtain the events, and convert them into raw events (RawEvent), and then The original event, after this event processing is completed, continue to loop and wait for the arrival of the input event

2. Then continue to call processEventsLocked to determine what type the original event belongs to, such as touch, mouse, keyboard, etc., and create the corresponding InputDevice

  1. Then continue to call processEventsForDeviceLocked to process InputDevice→process, and then continue to analyze TouchInputMapper.cpp, because this mapper is one of the types of InputDevice. According to the event type, determine whether to click or not. , sliding, multiple gestures, encapsulate event information into NotifyMotionArgs, encapsulate fingers into integer variables, representing the number of fingers, and then distribute them to InputDispatcher