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

preface

In the last article, we learned how to handle input events. Input events are sent to InputDispatcher for distribution. How does InputDispatcher handle input events? This article will give you the answer.

1. Processing type of InputReader

In this article, we know that InputReader will process the original input event. If the event type is keystroke type event, the following code will be called. frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
  ...
    boolneedWake; {... }// release lock
    if(needWake) { mLooper->wake(); }}Copy the code

The notifyKey method of InputDispatcher is used to wake up the InputDispatcherThread. Its parameter NotifyKeyArgs is derived from InputReader’s processing of keystroke events. frameworks/native/services/inputflinger/InputListener.h

struct NotifyKeyArgs : public NotifyArgs {
    nsecs_t eventTime;
    int32_t deviceId;
    uint32_t source;
    uint32_t policyFlags;
    int32_t action;
    int32_t flags;
    int32_t keyCode;
    int32_t scanCode;
    int32_t metaState;
    nsecs_t downTime;
    inline NotifyKeyArgs(a) { }
    NotifyKeyArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags,
            int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
            int32_t metaState, nsecs_t downTime);
    NotifyKeyArgs(const NotifyKeyArgs& other);
    virtual ~NotifyKeyArgs() { }
    virtual void notify(const sp<InputListenerInterface>& listener) const;
};
Copy the code

As you can see, the NotifyKeyArgs structure inherits from the NotifyArgs structure, as shown in the figure below.

2.InputDispatcher distribution process

Different event types have different distribution processes. Swich events are not distributed. In notifySwitch of InputDispatcher, Swich events are processed by InputDispatcherPolicy. This series of articles has always explained the key event related, this time, to the Motion event distribution process for example, the key event distribution event can be interested in looking at the source code, essentially the same.

2.1 wake up InputDispatcherThread

The notifyMotion function of InputDispatcher is used to wake up the InputDispatcherThread. frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) {
#if DEBUG_INBOUND_EVENT_DETAILS.#endif
    // Check whether the Motion event parameters are valid
    if(! validateMotionEvent(args->action, args->actionButton, args->pointerCount, args->pointerProperties)) {/ / 1
        return;
    }
    uint32_t policyFlags = args->policyFlags;
    policyFlags |= POLICY_FLAG_TRUSTED;
    mPolicy->interceptMotionBeforeQueueing(args->eventTime, /*byref*/ policyFlags);
    bool needWake;
    { // acquire lock
        mLock.lock();
        // Whether Motion events need to be filtered by InputFilter
        if (shouldSendMotionToInputFilterLocked(args)) {/ / 2
            mLock.unlock();
            MotionEvent event;
            // Initializes the MotionEvent, assigning the parameter information in NotifyMotionArgs to the parameter in MotionEvent
            event.initialize(args->deviceId, args->source, args->action, args->actionButton,
                    args->flags, args->edgeFlags, args->metaState, args->buttonState,
                    0.0, args->xPrecision, args->yPrecision,
                    args->downTime, args->eventTime,
                    args->pointerCount, args->pointerProperties, args->pointerCoords);
           // Indicates that the filter has been filtered
            policyFlags |= POLICY_FLAG_FILTERED;
            If false is returned, the event will not be distributed
            if(! mPolicy->filterInputEvent(&event, policyFlags)) {/ / 3
                return; // event was consumed by the filter
            }
            mLock.lock();
        }
        / * * * * / 4
        MotionEntry* newEntry = new MotionEntry(args->eventTime,
                args->deviceId, args->source, policyFlags,
                args->action, args->actionButton, args->flags,
                args->metaState, args->buttonState,
                args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime,
                args->displayId,
                args->pointerCount, args->pointerProperties, args->pointerCoords, 0.0);
        needWake = enqueueInboundEventLocked(newEntry);/ / 5
        mLock.unlock();
    } // release lock
    if (needWake) {
        mLooper->wake();/ / 6}}Copy the code

Comment 1 checks whether the Motion event parameters are valid. It checks whether the number of touch points pointerCount is within a reasonable range (not less than 1 or more than 16) and whether the touch point ID is within a reasonable range (not less than 0 or more than 31). Note 2: If a Motion event needs to be filtered by InputFilter, it initializes a MotionEvent. Its purpose is to construct a MotionEvent from NotifyMotionArgs event parameters. MotionEven is then filtered by the method in comment 3, and if false is returned, the Motion event is ignored. At note 4, a MotionEntry object is constructed from the event parameter information in NotifyMotionArgs. Annotation to five MotionEntry into enqueueInboundEventLocked function, its internal will MotionEntry added to InputDispatcher mInboundQueue queue at the end of needWake and return a value, Represents whether the InputDispatcherThread needs to be woken up. If so, the code in comment 6 is called to wake the InputDispatcherThread.

2.2 InputDispatcher Distribution

When InputDispatcherThread is awakened, the threadLoop function of InputDispatcherThread is executed: 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 ones. The current time is obtained at comment 3. Combined with comment 4, it is obtained that the InputDispatcherThread needs to sleep for timeoutMillis. Finally, Looper’s pollOnce function is called to put the InputDispatcherThread to sleep and its maximum sleep time is set to timeoutMillis. When an input event occurs, the InputReader wakes up the InputDispatcherThread from sleep and the InputDispatcher restarts the distribution of the input event. See how the dispatchOnceInnerLocked function at comment 2 does event distribution. frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    ...
    // If InputDispatcher is frozen, no dispatch operation is performed
    if (mDispatchFrozen) {
#if DEBUG_FOCUS
        ALOGD("Dispatch frozen. Waiting some more.");
#endif
        return;
    }
    // If isAppSwitchDue is true, operations such as the HOME button are not timely responded to
   bool isAppSwitchDue = mAppSwitchDueTime <= currentTime;/ / 1
    if (mAppSwitchDueTime < *nextWakeupTime) {/ / 2
        *nextWakeupTime = mAppSwitchDueTime;
    }
   // If there are no events to distribute, fetch an event from the mInboundQueue
    if (! mPendingEvent) {
        Return if mInboundQueue is empty and there are no events to distribute
        if (mInboundQueue.isEmpty()) {
            ...
            if(! mPendingEvent) {return; }}else {
            // If mInboundQueue is not empty, take EventEntry from the queue header and assign it to mPendingEvent
            mPendingEvent = mInboundQueue.dequeueAtHead();
            traceInboundQueueLengthLocked();
        }
        if(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { pokeUserActivityLocked(mPendingEvent); } resetANRTimeoutsLocked(); } ALOG_ASSERT(mPendingEvent ! =NULL);
    bool done = false;
    DropReason dropReason = DROP_REASON_NOT_DROPPED;/ / 3.switch (mPendingEvent->type) {/ / 4.case EventEntry::TYPE_MOTION: {
        MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
        // If there is no prompt response to the window switch operation
        if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) {
            dropReason = DROP_REASON_APP_SWITCH;
        }
        // The event expired
        if(dropReason == DROP_REASON_NOT_DROPPED && isStaleEventLocked(currentTime, typedEntry)) { dropReason = DROP_REASON_STALE;  }// Block other Windows from getting events
        if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) {
            dropReason = DROP_REASON_BLOCKED;
        }
        done = dispatchMotionLocked(currentTime, typedEntry,
                &dropReason, nextWakeupTime);/ / 5
        break;
    }
    default:
        ALOG_ASSERT(false);
        break;
    }

    if (done) {
        if(dropReason ! = DROP_REASON_NOT_DROPPED) { dropInboundEventLocked(mPendingEvent, dropReason); } mLastDropReason = dropReason;// Release the event object
        releasePendingEventLocked();/ / 6
        // Enable InputDispatcher to quickly process the next dispatch event
        *nextWakeupTime = LONG_LONG_MIN;/ / 7
}
Copy the code

InputDispatcher’s dispatchOnceInnerLocked function has a longer code that captures the main source code for Motion event distribution. We mainly did the following things.

  1. If the InputDispatcher is frozen, no dispatch operation is performed. The InputDispatcher has three states: Normal, frozen and disabled. This can be set using the setInputDispatchMode function of InputDispatcher.
  2. The mAppSwitchDueTime in comment 1 represents the latest distribution time of the action event when a window switch operation (such as pressing the Home button or hanging up the phone) occurred recently in the App. If mAppSwitchDueTime is less than or equal to the current system time, it indicates that the window switchover operation is not performed in time, and isAppSwitchDue is set to true. At note 2, if mAppSwitchDueTime is less than nextWakeupTime (the next time InputDispatcherThread wakes up), mAppSwitchDueTime is assigned to nextWakeupTime. In this way, after InputDispatcher processes the distribution event, the window switch operation will be handled first.
  3. Fetch event If there are no events to distribute, fetch an event from the mInboundQueue, return if the mInboundQueue is empty and there are no events to distribute, if the mInboundQueue is not empty, Take the EventEntry in the queue header and assign it to an mPendingEvent whose type is an EventEntry object pointer.
  4. Event discarding The dropReason in comment 3 represents the cause of event discarding. Its default value is DROP_REASON_NOT_DROPPED, indicating that the event is not discarded. Note 4 distinguishes the mPendingEvent according to its type, which mainly intercepts the Motion type. After filtering, the dispatchMotionLocked function at comment 5 is called to find the appropriate window for this event.
  5. Subsequent processing If comments five event distribution is successful, will call releasePendingEventLocked function in 6 place, its internal will mPendingEvent value is set to Null, and release mPendingEvent pointing to the object of memory. Note 7 sets the value of nextWakeupTime to LONG_LONG_MIN so that InputDispatcher can quickly process the next dispatch event.

Afterword.

This article explains the processing type of InputReader and the distribution process of InputDispatcher. Due to the length of this article, there is still a part of the distribution process of InputDispatcher, which is the process of event distribution to the target window, which will be explained in the next article of this series.


Share big front-end, Java, cross-platform technology, focus on career development and industry trends.