In this paper, starting from Liu Wangshu blog address: liuwangshu. Cn/framework/I…

WMS series in-depth understanding of JNI series input system series

preface

In the previous article, we looked at the birth of IMS and the startup of IMS. In this article, we will look at the startup process of IMS and the handling of input events.

1. Start the IMS

InputManagerService InputManagerService InputManagerService InputManagerService InputManagerService InputManagerService InputManagerService frameworks/base/services/java/com/android/server/SystemServer.java

 private void startOtherServices(a) {... traceBeginAndSlog("StartInputManagerService");
         inputManager = newInputManagerService(context); traceEnd(); . traceBeginAndSlog("StartInputManager");
         inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
         inputManager.start();
         traceEnd();

}
Copy the code

After the IMS is created, the IMS is started immediately. The start method of IMS is shown as follows. frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

 public void start(a) {
        Slog.i(TAG, "Starting input manager");
        nativeStart(mPtr);
        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this); . }Copy the code

In the START method of IMS, the IMS adds itself to the Watchdog to monitor key services (such as AMS and WMS). What is the function of the JNI layer corresponding to the nativeStart method? Check out the gInputManagerMethods array for com_android_server_input_InputManagerService. For those who don’t understand JNI, check out the JNI series. frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

static const JNINativeMethod gInputManagerMethods[] = {
...
   { "nativeStart"."(J)V",
            (void*) nativeStart },
...
}
Copy the code

NativeStart method corresponding to the JNI function for nativeStart: frameworks/base/services/core/JNI/com_android_server_input_InputManagerService CPP

static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
    status_t result = im->getInputManager()->start();/ / 1
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started."); }}Copy the code

Cast a PTR of type JLONG to its original type (the NativeInputManager pointer type) using the reinterpret_cast operator. The start function of InputManager is called at comment 1. frameworks/native/services/inputflinger/InputManager.cpp

status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }
    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    if (result) {
        ALOGE("Could not start InputReader thread due to error %d.", result);
        mDispatcherThread->requestExit();
        return result;
    }
    return OK;
}
Copy the code

The InputManager start function runs InputReaderThread and InputDispatcherThread. These two threads are mentioned in the Android input system (I) input event passing process and the birth of InputManagerService. They are created in the constructor of InputManager, where InputReaderThread runs InputReader. InputDispatcher is running in InputDispatcherThread.

2. Startup process of InputDispatcher

To review where InputDispatcher and InputReader are created, the constructor of InputManager is shown below. frameworks/native/services/inputflinger/InputManager.cpp

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}
Copy the code

You can see that InputDispatcher is associated with InputReader. InputDispatcher is passed in as a parameter to InputReader. The InputDispatcher is created before the InputReader. This order cannot be changed because you want to ensure that the InputDispatcher has already been created when the InputReader sends the processed input event to the InputDispatcher. The definition of InputDispatcher is as follows. frameworks/native/services/inputflinger/InputDispatcher.h

class InputDispatcherThread : public Thread {
public:
    explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher);
    ~InputDispatcherThread();
private:
    virtual bool threadLoop(a);
    sp<InputDispatcherInterface> mDispatcher;
};
}
Copy the code

ThreadLoop is a pure virtual function defined in inputDispatcher. h. InputDispatcher inherits Thread. A native Thread has a loop inside it that calls the threadLoop function when it runs. If it returns true and does not call requestExit, the threadLoop function will then be called. See how the threadLoop function of InputDispatcherThread is implemented. frameworks/native/services/inputflinger/InputDispatcher.cpp

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

ThreadLoop function calls only InputDispatcher dispatchOnce function: frameworks/native/services/inputflinger InputDispatcher. CPP

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        AutoMutex _l(mLock);
        mDispatcherIsAliveCondition.broadcast();
        if(! haveCommandsLocked()) {/ / 1
            dispatchOnceInnerLocked(&nextWakeupTime);/ / 2
        }
        if(runCommandsLockedInterruptible()) { nextWakeupTime = LONG_LONG_MIN; }}// release lock
    nsecs_t currentTime = now();/ / 3
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);/ / 4
    mLooper->pollOnce(timeoutMillis);
}
Copy the code

Comment 1 checks for commands waiting to be processed in the InputDispatcher’s cache queue. If not, the dispatchOnceInnerLocked function at Comment 2 is executed to distribute input events to the appropriate Window. Note 3 to obtain the current time, combined with note 4, the InputDispatcher needs to sleep time is timeoutMillis. Finally, Looper’s pollOnce function is called to put the InputDispatcher to sleep and its maximum sleep time is set to timeoutMillis. When an input event occurs, the InputReader wakes up the InputDispatcher from sleep and the InputDispatcher restarts the distribution of the input event. So how does InputReader wake up InputDispatcher? Let’s move on.

3.InputReader processes events

InputReader is started in InputReaderThread. InputReaderThread and InputDispatcherThread have similar definitions. They also inherit Thread and define threadLoop pure virtual functions. If the event being processed is a keyboard input event, the call sequence diagram is shown below.

frameworks/native/services/inputflinger/InputReader.cpp

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

ThreadLoop function calls only InputReader loopOnce function: frameworks/native/services/inputflinger InputReader. CPP

void InputReader::loopOnce() {
  ...
    // Obtain the event information by using the EventHub getEvents function and save it in the mEventBuffer
    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);/ / 1
    { // acquire lock
        AutoMutex _l(mLock);
        mReaderIsAliveCondition.broadcast();
        if (count) {
            // If there is event information, call processEventsLocked to process the event
            processEventsLocked(mEventBuffer, count);/ / 2}... }Copy the code

Note 1 calls the getEvents function of EventHub to obtain event information about a device node into the mEventBuffer. The event information can be added or deleted from a device node (device event) or the original input event. The processEventsLocked function in comment 2 is used to process the original input events in the mEventBuffer. The processed input events are sent to InputDispatcher. ProcessEventsLocked is shown below. frameworks/native/services/inputflinger/InputReader.cpp

void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
    // Walk through all the events
    for (const RawEvent* rawEvent = rawEvents; count;) {
        int32_t type = rawEvent->type;
        size_t batchSize = 1;
        // Event types are raw input events and device events. This conditional statement handles raw input events
        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: %d Count: %d", batchSize, count);
#endif
         // Handle raw input events for devices corresponding to deviceId
            processEventsForDeviceLocked(deviceId, rawEvent, batchSize);/ / 1
        } else {
        // Handle device events
            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; }}Copy the code

InputReader’s processEventsLocked function first iterates through all the events represented as RawEvent objects, separating the raw input events from the device events, Device events are classified as DEVICE_ADDED, DEVICE_REMOVED, and FINISHED_DEVICE_SCAN. These events are generated in the EventHub getEvent function. If the event is DEVICE_ADDED, InputReader creates an InputDevice object to store device information and stores the InputDevice in a KeyedVector container called mDevices. The same equipment input events to processEventsForDeviceLocked function to deal with. frameworks/native/services/inputflinger/InputReader.cpp

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);/ / 1
    if (deviceIndex < 0) {
        ALOGW("Discarding event for unknown deviceId %d.", deviceId);
        return;
    }
    InputDevice* device = mDevices.valueAt(deviceIndex);/ / 2
    if (device->isIgnored()) {
        //ALOGD("Discarding event for ignored deviceId %d.", deviceId);
        return;
    }
    device->process(rawEvents, count);
}
Copy the code

In Comment 1, the corresponding deviceIndex is obtained from mDevices based on deviceId. In Comment 2, the corresponding InputDevice is obtained from mDevices based on deviceIndex. Finally invokes the InputDevice process function: frameworks/native/services/inputflinger InputReader. CPP

void InputDevice::process(const RawEvent* rawEvents, size_t count) {*
    size_t numMappers = mMappers.size();
    // Iterate through all events of the InputDevice
    for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
#if DEBUG_RAW_EVENTS
        ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld",
                rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
                rawEvent->when);
#endif
        //mDropUntilNextSync defaults to false and is set to true if the device's input event buffer overflows.
        if (mDropUntilNextSync) {
            ...
        } else {
            for (size_t i = 0; i < numMappers; i++) {/ / 1
                InputMapper* mapper = mMappers[i];
                mapper->process(rawEvent);/ / 2}}}}Copy the code

First, it iterates through all the events in InputDevice. The object that actually processes the raw input events is the InputMapper object. Since there are many types of raw input events, there are many subclasses in InputMapper for processing different raw input events. For example, KeyboardInputMapper is used to handle keyboard input events and TouchInputMapper is used to handle touch input events. Comment 1 iterates through all the Inputmappers, and comment 2 turns the original input event over to those Inputmappers. The InputReader doesn’t care which InputMapper does the processing. For example, the KeyboardInputMapper process function is shown below. frameworks/native/services/inputflinger/InputReader.cpp

void KeyboardInputMapper::process(const RawEvent* rawEvent) {
    switch (rawEvent->type) {
    case EV_KEY: {/ / 1
        int32_t scanCode = rawEvent->code;
        int32_t usageCode = mCurrentHidUsage;
        mCurrentHidUsage = 0;
        if(isKeyboardOrGamepadKey(scanCode)) { processKey(rawEvent->when, rawEvent->value ! =0, scanCode, usageCode);/ / 2
        }
        break; }... }}Copy the code

The KeyboardInputMapper processKey function at comment 2 is called if the event type is a KeyboardInputMapper event at comment 1. frameworks/native/services/inputflinger/InputReader.cpp

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode) { ... 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); / / 1}Copy the code

The processKey function encapsulates the processed keyboard input event as a NotifyKeyArgs, notifying the InputListenerInterface with a NotifyKeyArgs. InputDispatcher inherits InputDispatcherInterface, InputDispatcherInterface inherits InputListenerInterface, Therefore, note 1 actually calls the notifyKey function of InputDispatcher and sends the NotifyKeyArgs to InputDispatcher. frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
  ...
    bool needWake;
    { // acquire lock
        mLock.lock();
        if (shouldSendKeyToInputFilterLocked(args)) {
            mLock.unlock();
            policyFlags |= POLICY_FLAG_FILTERED;
            if(! mPolicy->filterInputEvent(&event, policyFlags)) {return; // event was consumed by the filter
            }
            mLock.lock();
        }
        int32_t repeatCount = 0;
        KeyEntry* newEntry = new KeyEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, flags, keyCode, args->scanCode,
                metaState, repeatCount, args->downTime);/ / 1
        needWake = enqueueInboundEventLocked(newEntry);/ / 2
        mLock.unlock();
    } // release lock
    if(needWake) { mLooper->wake(); }}Copy the code

The code block takes the form of a Mutex Mutex lock. According to NotifyKeyArgs at comment 1, a KeyEntry object is reencapsulated to represent the keystroke data. Note 2 determines whether it is necessary to wake up the InputDispatcher from sleep according to KeyEntry. If it is necessary, call Looper’s wake function to wake up the InputDispatcher. After being woken up, the InputDispatcher will re-distribute input events.

conclusion

This article covers four key classes, IMS, EventHub, InputDispatcher, and InputReader, which do the following:

  1. The IMS enables InputDispatcherThread and InputReaderThread to run InputDispatcher and InputReader respectively.
  2. InputDispatcher is created before InputReader, and InputDispatcher’s dispatchOnceInnerLocked function is used to distribute events to the appropriate Windows. InputDispatcher goes to sleep when there is no input event processing and waits for the InputReader notification to wake up.
  3. The InputReader uses EventHub’s getEvents function to retrieve event information. If the event is a raw input event, the InputMapper dispatches the raw input event to a different InputMapper, and finally to the InputDispatcher for distribution.
  4. The notifyKey function of InputDispatcher determines whether the InputDispatcher is to be awakened according to the key data. After the InputDispatcher is awakened, The dispatchOnceInnerLocked function is re-called to distribute the input event to the appropriate Window.