Loading services/inputflinger/dispatcher/InputDispatcher.cpp +30 −1 Original line number Diff line number Diff line Loading @@ -480,6 +480,33 @@ void InputDispatcher::dispatchOnce() { mLooper->pollOnce(timeoutMillis); } /** * Raise ANR if there is no focused window. * Before the ANR is raised, do a final state check: * 1. The currently focused application must be the same one we are waiting for. * 2. Ensure we still don't have a focused window. */ void InputDispatcher::processNoFocusedWindowAnrLocked() { // Check if the application that we are waiting for is still focused. std::shared_ptr<InputApplicationHandle> focusedApplication = getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId); if (focusedApplication == nullptr || focusedApplication->getApplicationToken() != mAwaitedFocusedApplication->getApplicationToken()) { // Unexpected because we should have reset the ANR timer when focused application changed ALOGE("Waited for a focused window, but focused application has already changed to %s", focusedApplication->getName().c_str()); return; // The focused application has changed. } const sp<InputWindowHandle>& focusedWindowHandle = getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId); if (focusedWindowHandle != nullptr) { return; // We now have a focused window. No need for ANR. } onAnrLocked(mAwaitedFocusedApplication); } /** * Check if any of the connections' wait queues have events that are too old. * If we waited for events to be ack'ed for more than the window timeout, raise an ANR. Loading @@ -491,8 +518,9 @@ nsecs_t InputDispatcher::processAnrsLocked() { // Check if we are waiting for a focused window to appear. Raise ANR if waited too long if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) { if (currentTime >= *mNoFocusedWindowTimeoutTime) { onAnrLocked(mAwaitedFocusedApplication); processNoFocusedWindowAnrLocked(); mAwaitedFocusedApplication.reset(); mNoFocusedWindowTimeoutTime = std::nullopt; return LONG_LONG_MIN; } else { // Keep waiting Loading Loading @@ -1494,6 +1522,7 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, DEFAULT_INPUT_DISPATCHING_TIMEOUT); mNoFocusedWindowTimeoutTime = currentTime + timeout.count(); mAwaitedFocusedApplication = focusedApplicationHandle; mAwaitedApplicationDisplayId = displayId; ALOGW("Waiting because no window has focus but %s may eventually add a " "window when it finishes starting up. Will wait for %" PRId64 "ms", mAwaitedFocusedApplication->getName().c_str(), millis(timeout)); Loading services/inputflinger/dispatcher/InputDispatcher.h +5 −0 Original line number Diff line number Diff line Loading @@ -390,6 +390,11 @@ private: * Used to raise an ANR when we have no focused window. */ std::shared_ptr<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock); /** * The displayId that the focused application is associated with. */ int32_t mAwaitedApplicationDisplayId GUARDED_BY(mLock); void processNoFocusedWindowAnrLocked() REQUIRES(mLock); /** * This map will store the pending focus requests that cannot be currently processed. This can Loading services/inputflinger/tests/InputDispatcher_test.cpp +62 −0 Original line number Diff line number Diff line Loading @@ -3483,6 +3483,68 @@ TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) { mFocusedWindow->assertNoEvents(); } /** * If we have no focused window, and a key comes in, we start the ANR timer. * The focused application should add a focused window before the timer runs out to prevent ANR. * * If the user touches another application during this time, the key should be dropped. * Next, if a new focused window comes in, without toggling the focused application, * then no ANR should occur. * * Normally, we would expect the new focused window to be accompanied by 'setFocusedApplication', * but in some cases the policy may not update the focused application. */ TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) { std::shared_ptr<FakeApplicationHandle> focusedApplication = std::make_shared<FakeApplicationHandle>(); focusedApplication->setDispatchingTimeout(60ms); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication); // The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused. mFocusedWindow->setFocusable(false); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}}); mFocusedWindow->consumeFocusEvent(false); // Send a key. The ANR timer should start because there is no focused window. // 'focusedApplication' will get blamed if this timer completes. // Key will not be sent anywhere because we have no focused window. It will remain pending. int32_t result = injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result); // Wait until dispatcher starts the "no focused window" timer. If we don't wait here, // then the injected touches won't cause the focused event to get dropped. // The dispatcher only checks for whether the queue should be pruned upon queueing. // If we inject the touch right away and the ANR timer hasn't started, the touch event would // simply be added to the queue without 'shouldPruneInboundQueueLocked' returning 'true'. // For this test, it means that the key would get delivered to the window once it becomes // focused. std::this_thread::sleep_for(10ms); // Touch unfocused window. This should force the pending key to get dropped. NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {UNFOCUSED_WINDOW_LOCATION}); mDispatcher->notifyMotion(&motionArgs); // We do not consume the motion right away, because that would require dispatcher to first // process (== drop) the key event, and by that time, ANR will be raised. // Set the focused window first. mFocusedWindow->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}}); setFocusedWindow(mFocusedWindow); mFocusedWindow->consumeFocusEvent(true); // We do not call "setFocusedApplication" here, even though the newly focused window belongs // to another application. This could be a bug / behaviour in the policy. mUnfocusedWindow->consumeMotionDown(); ASSERT_TRUE(mDispatcher->waitForIdle()); // Should not ANR because we actually have a focused window. It was just added too slowly. ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyAnrWasNotCalled()); } // These tests ensure we cannot send touch events to a window that's positioned behind a window // that has feature NO_INPUT_CHANNEL. // Layout: Loading Loading
services/inputflinger/dispatcher/InputDispatcher.cpp +30 −1 Original line number Diff line number Diff line Loading @@ -480,6 +480,33 @@ void InputDispatcher::dispatchOnce() { mLooper->pollOnce(timeoutMillis); } /** * Raise ANR if there is no focused window. * Before the ANR is raised, do a final state check: * 1. The currently focused application must be the same one we are waiting for. * 2. Ensure we still don't have a focused window. */ void InputDispatcher::processNoFocusedWindowAnrLocked() { // Check if the application that we are waiting for is still focused. std::shared_ptr<InputApplicationHandle> focusedApplication = getValueByKey(mFocusedApplicationHandlesByDisplay, mAwaitedApplicationDisplayId); if (focusedApplication == nullptr || focusedApplication->getApplicationToken() != mAwaitedFocusedApplication->getApplicationToken()) { // Unexpected because we should have reset the ANR timer when focused application changed ALOGE("Waited for a focused window, but focused application has already changed to %s", focusedApplication->getName().c_str()); return; // The focused application has changed. } const sp<InputWindowHandle>& focusedWindowHandle = getFocusedWindowHandleLocked(mAwaitedApplicationDisplayId); if (focusedWindowHandle != nullptr) { return; // We now have a focused window. No need for ANR. } onAnrLocked(mAwaitedFocusedApplication); } /** * Check if any of the connections' wait queues have events that are too old. * If we waited for events to be ack'ed for more than the window timeout, raise an ANR. Loading @@ -491,8 +518,9 @@ nsecs_t InputDispatcher::processAnrsLocked() { // Check if we are waiting for a focused window to appear. Raise ANR if waited too long if (mNoFocusedWindowTimeoutTime.has_value() && mAwaitedFocusedApplication != nullptr) { if (currentTime >= *mNoFocusedWindowTimeoutTime) { onAnrLocked(mAwaitedFocusedApplication); processNoFocusedWindowAnrLocked(); mAwaitedFocusedApplication.reset(); mNoFocusedWindowTimeoutTime = std::nullopt; return LONG_LONG_MIN; } else { // Keep waiting Loading Loading @@ -1494,6 +1522,7 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, DEFAULT_INPUT_DISPATCHING_TIMEOUT); mNoFocusedWindowTimeoutTime = currentTime + timeout.count(); mAwaitedFocusedApplication = focusedApplicationHandle; mAwaitedApplicationDisplayId = displayId; ALOGW("Waiting because no window has focus but %s may eventually add a " "window when it finishes starting up. Will wait for %" PRId64 "ms", mAwaitedFocusedApplication->getName().c_str(), millis(timeout)); Loading
services/inputflinger/dispatcher/InputDispatcher.h +5 −0 Original line number Diff line number Diff line Loading @@ -390,6 +390,11 @@ private: * Used to raise an ANR when we have no focused window. */ std::shared_ptr<InputApplicationHandle> mAwaitedFocusedApplication GUARDED_BY(mLock); /** * The displayId that the focused application is associated with. */ int32_t mAwaitedApplicationDisplayId GUARDED_BY(mLock); void processNoFocusedWindowAnrLocked() REQUIRES(mLock); /** * This map will store the pending focus requests that cannot be currently processed. This can Loading
services/inputflinger/tests/InputDispatcher_test.cpp +62 −0 Original line number Diff line number Diff line Loading @@ -3483,6 +3483,68 @@ TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) { mFocusedWindow->assertNoEvents(); } /** * If we have no focused window, and a key comes in, we start the ANR timer. * The focused application should add a focused window before the timer runs out to prevent ANR. * * If the user touches another application during this time, the key should be dropped. * Next, if a new focused window comes in, without toggling the focused application, * then no ANR should occur. * * Normally, we would expect the new focused window to be accompanied by 'setFocusedApplication', * but in some cases the policy may not update the focused application. */ TEST_F(InputDispatcherMultiWindowAnr, FocusedWindowWithoutSetFocusedApplication_NoAnr) { std::shared_ptr<FakeApplicationHandle> focusedApplication = std::make_shared<FakeApplicationHandle>(); focusedApplication->setDispatchingTimeout(60ms); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication); // The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused. mFocusedWindow->setFocusable(false); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}}); mFocusedWindow->consumeFocusEvent(false); // Send a key. The ANR timer should start because there is no focused window. // 'focusedApplication' will get blamed if this timer completes. // Key will not be sent anywhere because we have no focused window. It will remain pending. int32_t result = injectKey(mDispatcher, AKEY_EVENT_ACTION_DOWN, 0 /*repeatCount*/, ADISPLAY_ID_DEFAULT, INPUT_EVENT_INJECTION_SYNC_NONE, 10ms /*injectionTimeout*/); ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, result); // Wait until dispatcher starts the "no focused window" timer. If we don't wait here, // then the injected touches won't cause the focused event to get dropped. // The dispatcher only checks for whether the queue should be pruned upon queueing. // If we inject the touch right away and the ANR timer hasn't started, the touch event would // simply be added to the queue without 'shouldPruneInboundQueueLocked' returning 'true'. // For this test, it means that the key would get delivered to the window once it becomes // focused. std::this_thread::sleep_for(10ms); // Touch unfocused window. This should force the pending key to get dropped. NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {UNFOCUSED_WINDOW_LOCATION}); mDispatcher->notifyMotion(&motionArgs); // We do not consume the motion right away, because that would require dispatcher to first // process (== drop) the key event, and by that time, ANR will be raised. // Set the focused window first. mFocusedWindow->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mFocusedWindow, mUnfocusedWindow}}}); setFocusedWindow(mFocusedWindow); mFocusedWindow->consumeFocusEvent(true); // We do not call "setFocusedApplication" here, even though the newly focused window belongs // to another application. This could be a bug / behaviour in the policy. mUnfocusedWindow->consumeMotionDown(); ASSERT_TRUE(mDispatcher->waitForIdle()); // Should not ANR because we actually have a focused window. It was just added too slowly. ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyAnrWasNotCalled()); } // These tests ensure we cannot send touch events to a window that's positioned behind a window // that has feature NO_INPUT_CHANNEL. // Layout: Loading