Analysis of the principle of systemserver's inputdispatcher directly generating CANCEL events - annoying android touch interview questions

Background review:

The previous blog has focused on explaining the Cancel touch event generated at the app level. The general principle is as follows:
Previous blog address: https://blog.csdn.net/learnframework/article/details/124086882
Insert image description here You can see that in In fact, the touch event passed by the server system server is still move, but after the move event reaches the app side, due to the app side's own business, the passed move event is turned into cancel. Video explanation:
https://www.bilibili.com/video /BV1nY4y1e713/
Insert image description here So the question arises? Does this mean that when the systemserver delivers the event, it has become cancelled?
As shown below:

Insert image description here

The answer is of course yes. Let’s do a detailed analysis below

2. The system server side becomes a cancel event.

Reproduction scenario:
1. Set the mobile phone to navigation button mode and click on the desktop
Insert image description here

2. Click an application to enter, then keep touching the application with your finger.
3. Then click the navigation key home button with the other hand to return to the desktop.

The above 3 steps can reproduce the application that clicks in and receives a Cancel event. Because the hand is actually always touching the screen, of course there is no up received. However, after all, the application has been retreated to the background at this time, so it can only be given. A cancel event to the application. This cancel event is passed to the application by the input dispatcher in the system server.

Let's perform source code analysis. The cancel event is generated in inputdispatcher.

1. Open the log
and open the DEBUG_OUTBOUND_EVENT_DETAILS log. You can use the adb command here or directly modify it to true.

/**
 * Log detailed debug messages about each outbound event processed by the dispatcher.
 * Enable this via "adb shell setprop log.tag.InputDispatcherOutboundEvent DEBUG" (requires restart)
 */
const bool DEBUG_OUTBOUND_EVENT_DETAILS =
       true;// __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "OutboundEvent", ANDROID_LOG_INFO);

2. Check the log when reoccurring

09-10 22:49:50.775  2231  2357 D InputDispatcher: channel 'a1b72df com.android.messaging/com.android.messaging.ui.conversationlist.ConversationListActivity (server)' ~ Synthesized 1 cancelation events to bring channel back in sync with reality: touched window was removed, mode=1.

Here you can see that a cancel event is synchronized to com.android.messaging/com.android.messaging.ui.conversationlist.ConversationListActivity. Please note that the reason is "touched window was removed"

You can trace the relevant code based on this reason:

test@test:~/nx563j_xiaomi/frameworks/native$ grep "touched window was removed" ./ -rn
./services/inputflinger/dispatcher/InputDispatcher.cpp:4759:                                               "touched window was removed");

Found line 4759 in InputDispatcher:

/**
 * Called from InputManagerService, update window handle list by displayId that can receive input.
 * A window handle contains information about InputChannel, Touch Region, Types, Focused,...
 * If set an empty list, remove all handles from the specific display.
 * For focused handle, check if need to change and send a cancel event to previous one.
 * For removed handle, check if need to send a cancel event if already in touch.
 */
void InputDispatcher::setInputWindowsLocked(
        const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) {
    
    
   //省略部分
   //把inputdispatcher的window相关信息变成最新
       updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);

   //最为关键的mTouchStatesByDisplay变量,一般保存就是当前触摸事件的派发情况,主要保存了派发触摸相关的window信息
    std::unordered_map<int32_t, TouchState>::iterator stateIt =
            mTouchStatesByDisplay.find(displayId);
    if (stateIt != mTouchStatesByDisplay.end()) {
    
    
        TouchState& state = stateIt->second;
        for (size_t i = 0; i < state.windows.size();) {
    
    
            TouchedWindow& touchedWindow = state.windows[i];
            //拿正在触摸的window信息与最新的window的信息比较看看是否还存在,如果不在说明消失了
            if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) {
    
    
              
                std::shared_ptr<InputChannel> touchedInputChannel =
                        getInputChannelLocked(touchedWindow.windowHandle->getToken());
                if (touchedInputChannel != nullptr) {
    
    
                //开始触发相关的cancel事件
                    CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
                                               "touched window was removed");
                    synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
               
                    }
                }
                state.windows.erase(state.windows.begin() + i);
            } else {
    
    
                ++i;
            }
        }
//省略
}


The call to setInputWindowsLocked is mainly triggered when there is a window update in the system. For example, in the scene we demonstrated above, the application screen needs to exit the background after pressing the home button. At this time, the application window must be gone, and the change method will be triggered.
1. updateWindowHandlesForDisplayLocked
will update the latest window information to the mWindowHandlesByDisplay variable of inputdispatcher.
2. The main variable in the method has mTouchStatesByDisplay:

The most critical mTouchStatesByDisplay variable generally saves the current dispatch of touch events. It mainly saves the window information related to dispatching touches.

That is, it represents the record of the window related to the current touch event dispatch.

3. There is another key method getWindowHandleLocked

sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked(
        const sp<IBinder>& windowHandleToken) const {
    
    
    if (windowHandleToken == nullptr) {
    
    
        return nullptr;
    }
		//就是拿传入的windowHandleToken去mWindowHandlesByDisplay遍历看看是否有
    for (auto& it : mWindowHandlesByDisplay) {
    
    
        const std::vector<sp<WindowInfoHandle>>& windowHandles = it.second;
        for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
    
    
            if (windowHandle->getToken() == windowHandleToken) {
    
    
                return windowHandle;
            }
        }
    }
    return nullptr;
}

4. Find the input channel corresponding to the deleted window and pass the corresponding cancel event
//Get touchedInputChannel
std::shared_ptr touchedInputChannel =
getInputChannelLocked(touchedWindow.windowHandle->getToken());
//Send cancel event to touchedInputChannel
synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options) ;

If it is found that the window that is dispatching events is no longer in the updated window, it means that the window has been removed, and then the relevant cancel event will be triggered to the original window.

Finally, for more useful information, you can directly contact Qianlima with +w; androidframework007

Guess you like

Origin blog.csdn.net/learnframework/article/details/132797212