Android_input System Analysis InputReader

Posted by BluntedbyNature on Mon, 16 Sep 2019 10:06:42 +0200

Previous analysis said that getEvent() returned the original result to InputReader. Today, for InputReader, the results returned by getevent are analyzed:

Let's first look at where InputReader calls the getEvent() method.

void InputReader::loopOnce() {
    ...
    { // acquire lock
        AutoMutex _l(mLock);
             ...
            timeoutMillis = 0;
            // Update configuration information if configuration information has been modified
            refreshConfigurationLocked(changes);
   ...
    } // release lock
    ....
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
    ...
            processEventsLocked(mEventBuffer, count);
  
            getInputDevicesLocked(inputDevices);
    ....
    } // release lock

    // Send out a message that the describes the changed input devices.
    if (inputDevicesChanged) {
        mPolicy->notifyInputDevicesChanged(inputDevices);
    }
    ...
    mQueuedListener->flush();
}

First of all, we need to know that this loopOnce() is executed in InputReaderThread. InputReaderThread is played in InputManager.

In this loopOne method, we can see that the following things need to be done:

  • See if the InputReader configuration has changed
  • Read events from EventHub, where EVENT_BUFFER_SIZE = 256
  • Call the processEventsLocked() method to simply handle events
  • Call the getInputDeviceLocked() method to get information about the input device
  • Messages are sent through the mPolicy - > notifyInputDeviceChanged () method to notify the input reader that some input devices have changed and to provide information about all current input devices.
  • Pass events to InputDispatcher through the flush() method

1.processEventsLocked

In the process EventsLocked () method, the event type should be judged first. If the device is scanned by adding or deleting, the corresponding method should be called to deal with it. Here, the scan of adding or deleting is not analyzed first, and the registered event trigger is mainly processed. Type < EventHubInterface:: FIRST_SYNTHETIC_EVENT requires event processing. Process Events ForDeviceLocked () is called for processing.

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    // Processing in for loop
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        // If the event type is judged, it will be handled only if the condition is satisfied, when the type < EventHubInterface:: FIRST_SYNTHETIC_EVENT
        // Handling events as needed
        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;
            }
#if DEBUG_RAW_EVENTS
            ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
#endif
            //Processing by calling process Events ForDeviceLocked
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
        } else {
            //If the following types are used to explain the addition, deletion and scanning of devices, then the corresponding methods are called to process them.
            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;
    }
}

Next, let's look at what the processEvents ForDeviceLocked () method does.

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex < 0) {
        ALOGW("Discarding event for unknown deviceId %d.", deviceId);
        return;
    }

    InputDevice* device = mDevices.valueAt(deviceIndex);
    if (device->isIgnored()) {
        //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }

    device->process(rawEvents, count);
}

You can see that the device event type corresponding to InputDevice is found in process Events ForDeviceLocked () by deviceId, and then you can judge whether you want to ignore the event or not. If not, you will call InputDevice's process () method for processing. Let's continue to see what InputDevice's process () does:

// Here we use the corresponding mapper program for all events.
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.
    size_t numMappers = mMappers.size();
    for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
#if DEBUG_RAW_EVENTS
        ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
                rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
                rawEvent->when);
#endif

        if (mDropUntilNextSync) {
            if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
                mDropUntilNextSync = false;
#if DEBUG_RAW_EVENTS
                ALOGD("Recovered from input event buffer overrun.");
#endif
            } else {
#if DEBUG_RAW_EVENTS
                ALOGD("Dropped input event while waiting for next input sync.");
#endif
            }
        } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
            ALOGI("Detected input event buffer overrun for device %s.", getName().string());
            mDropUntilNextSync = true;
            reset(rawEvent->when);
        } else {
            // If events do not need to be discarded and processed, all mappers will be facilitated and their corresponding mappers will be processed.
            for (size_t i = 0; i < numMappers; i++) {
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);
            }
        }
        --count;
    }
}

The above code mainly calls all Mapper and calls the process() method of each Mapper for processing. Of course, not all Mapper will handle an event, only Mapper corresponding to the event will handle it. So how many Mappers are there?

We can see that there is a very corresponding Mapper:(only listed below) in the InputReader.h header file.

...
class VibratorInputMapper : public InputMapper {
...}
...
class KeyboardInputMapper : public InputMapper {
...}
...

class CursorInputMapper : public InputMapper {
...}
...
class RotaryEncoderInputMapper : public InputMapper {
...}
...
class TouchInputMapper : public InputMapper {
...}
...
class ExternalStylusInputMapper : public InputMapper {
...}
...
class JoystickInputMapper : public InputMapper {
...}
...

Here's an example of TouchInputMapper, which uses more touch types on Android devices (I regret it). This is too much. I want to find a simple analysis. Let's take KeyBoardInputMapper as an example to see how to deal with it.

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)) {
            processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode);
        }
        break;
    }
    case EV_MSC: {
        if (rawEvent->code == MSC_SCAN) {
            mCurrentHidUsage = rawEvent->value;
        }
        break;
    }
    case EV_SYN: {
        if (rawEvent->code == SYN_REPORT) {
            mCurrentHidUsage = 0;
        }
    }
    }
}

In the proces() code of keyboard InputMapper, we can see its main work:

When RawEvent - > type is EV_KEY, the key class Input event is specified, and processKey is called to process it.

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

    if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState,
                              &keyCode, &keyMetaState, &policyFlags)) {
        keyCode = AKEYCODE_UNKNOWN;
        keyMetaState = mMetaState;
        policyFlags = 0;
    }

    if (down) {
        ...
    } else {
       ...
    }

    if (updateMetaStateIfNeeded(keyCode, down)) {
     
        keyMetaState = mMetaState;
    }

    nsecs_t downTime = mDownTime;

    
    if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) {
        policyFlags |= POLICY_FLAG_WAKE;
    }

    if (mParameters.handlesKeyRepeat) {
        policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
    }

    NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
            down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
            AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime);
    getListener()->notifyKey(&args);
}

In the processKey () method:

  1. Call EventHub's mapKey function to get keyCode, keyMetaState, policy Flags
  2. Recording and logical judgment of press-up and press-up
  3. Encapsulate key event related information into NotifyKey Args (deviceId, when, policy Flags, down or up, keyCode, etc.)
  4. Finally, call getListener () - > notifyKey, where getListener gets InputDispatcher

Calling getListener () - > notifyKey (& args) calls the notifyKey function of QueuedInputListener. There are many notifyXXX functions in it. All you do is put NotifyXXXArgs into its mArgsQueue queue to store and wait for processing.

 

Back to process Events Locked, when the event type is add, delete and scan, it needs to be processed separately.

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

Take addDeviceLocked () to see how it will be handled

void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex >= 0) {
        ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId);
        return;
    }

    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId);
    uint32_t classes = mEventHub->getDeviceClasses(deviceId);
    int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId);
    // Create new InputDevice objects through deviceId, controllerNumber, identifier, classes
    InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes);
    device->configure(when, &mConfig, 0);
    device->reset(when);

    if (device->isIgnored()) {
        ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId,
                identifier.name.string());
    } else {
        ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId,
                identifier.name.string(), device->getSources());
    }
    // Place the newly created InputDevice in mDevices
    mDevices.add(deviceId, device);
    bumpGenerationLocked();

    if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) {
        notifyExternalStylusPresenceChanged();
    }
}

The main task of addDeviceLocked is to create a new InputDevice and then to put the newly created InputDevice object into mDevices.

Similarly, removeDeviceLocked needs to delete mDevices from the corresponding InputDevice

void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) {
    InputDevice* device = NULL;
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);
    if (deviceIndex < 0) {
        ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId);
        return;
    }

    device = mDevices.valueAt(deviceIndex);
    mDevices.removeItemsAt(deviceIndex, 1);
    bumpGenerationLocked();

....
    device->reset(when);
    delete device;
}

The job of handleConfiguration ChangedLocked () is to reset the global metastate, modify the configuration list, and notify mQueuedListener.

void InputReader::handleConfigurationChangedLocked(nsecs_t when) {
    // Reset global meta state because it depends on the list of all configured devices.
    updateGlobalMetaStateLocked();

    // Enqueue configuration changed.
    NotifyConfigurationChangedArgs args(when);
    mQueuedListener->notifyConfigurationChanged(&args);
}

2.getInputDevicesLocked

void InputReader::getInputDevicesLocked(Vector<InputDeviceInfo>& outInputDevices) {
    outInputDevices.clear();

    size_t numDevices = mDevices.size();
    for (size_t i = 0; i < numDevices; i++) {
        InputDevice* device = mDevices.valueAt(i);
        if (!device->isIgnored()) {
            outInputDevices.push();
            device->getDeviceInfo(&outInputDevices.editTop());
        }
    }
}

It simply takes out the InputDevice that does not need to be ignored in mDevices and puts it into the parameter outInputDevices, which is inputDevices in loopOne.

When the InputDevice is added, the notifyInputDevicesChanged function of InputManagerService is called to inform the system that the input device information needs to be updated.

Processing equipment addition and deletion, preprocessing, categorizing events, putting events into the event queue, notifying the system to update equipment information, of course, is to notify Input Dispatcher to take out the events in the event queue for processing.

3. mQueuedListener->flush()

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() passes the event to InputDispatcher.

In flush, a loop takes out the NotifyXXXArgs in the queue (there are many kinds, such as NotifyKey Args, NotifyMotionArgs), calls its notify function to notify mInnerListener, which is the listener passed in when InputReader was created, that is, InputDispatcher, and eventually calls the notifyXXX function of mInnerListener.

Topics: JoyStick Android