Merge "Add focusTransferTarget in WindowInfo" into udc-dev
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 804ce4f..6df9ff1 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -125,6 +125,7 @@
         parcel->writeBool(replaceTouchableRegionWithCrop) ?:
         parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?:
         parcel->writeStrongBinder(windowToken);
+        parcel->writeStrongBinder(focusTransferTarget);
     // clang-format on
     return status;
 }
@@ -175,7 +176,9 @@
         parcel->read(touchableRegion) ?:
         parcel->readBool(&replaceTouchableRegionWithCrop) ?:
         parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?:
-        parcel->readNullableStrongBinder(&windowToken);
+        parcel->readNullableStrongBinder(&windowToken) ?:
+        parcel->readNullableStrongBinder(&focusTransferTarget);
+
     // clang-format on
 
     if (status != OK) {
diff --git a/libs/gui/android/gui/FocusRequest.aidl b/libs/gui/android/gui/FocusRequest.aidl
index b13c600..62d1b68 100644
--- a/libs/gui/android/gui/FocusRequest.aidl
+++ b/libs/gui/android/gui/FocusRequest.aidl
@@ -24,15 +24,6 @@
     @nullable IBinder token;
     @utf8InCpp String windowName;
     /**
-     * The token that the caller expects currently to be focused. If the
-     * specified token does not match the currently focused window, this request will be dropped.
-     * If the specified focused token matches the currently focused window, the call will succeed.
-     * Set this to "null" if this call should succeed no matter what the currently focused token
-     * is.
-     */
-    @nullable IBinder focusedToken;
-    @utf8InCpp String focusedWindowName;
-    /**
      * SYSTEM_TIME_MONOTONIC timestamp in nanos set by the client (wm) when requesting the focus
      * change. This determines which request gets precedence if there is a focus change request
      * from another source such as pointer down.
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index b01a3db..70b2ee8 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -236,6 +236,11 @@
     Type layoutParamsType = Type::UNKNOWN;
     ftl::Flags<Flag> layoutParamsFlags;
 
+    // The input token for the window to which focus should be transferred when this input window
+    // can be successfully focused. If null, this input window will not transfer its focus to
+    // any other window.
+    sp<IBinder> focusTransferTarget;
+
     void setInputConfig(ftl::Flags<InputConfig> config, bool value);
 
     void addTouchableRegion(const Rect& region);
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 5f80c16..9e8c65c 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -272,8 +272,6 @@
         FocusRequest request;
         request.token = mInputInfo.token;
         request.windowName = mInputInfo.name;
-        request.focusedToken = nullptr;
-        request.focusedWindowName = "";
         request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
         request.displayId = displayId;
         t.setFocusedWindow(request);
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index c51b244..11b87ef 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -71,6 +71,7 @@
     i.applicationInfo.name = "ApplicationFooBar";
     i.applicationInfo.token = new BBinder();
     i.applicationInfo.dispatchingTimeoutMillis = 0x12345678ABCD;
+    i.focusTransferTarget = new BBinder();
 
     Parcel p;
     i.writeToParcel(&p);
@@ -101,6 +102,7 @@
     ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop);
     ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle);
     ASSERT_EQ(i.applicationInfo, i2.applicationInfo);
+    ASSERT_EQ(i.focusTransferTarget, i2.focusTransferTarget);
 }
 
 TEST(InputApplicationInfo, Parcelling) {
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
index 4da846b..0e4e79e 100644
--- a/services/inputflinger/dispatcher/FocusResolver.cpp
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <optional>
 #define LOG_TAG "InputDispatcher"
 #define ATRACE_TAG ATRACE_TAG_INPUT
 
@@ -25,6 +26,7 @@
 #include <binder/Binder.h>
 #include <ftl/enum.h>
 #include <gui/WindowInfo.h>
+#include <unordered_set>
 
 #include "DebugConfig.h"
 #include "FocusResolver.h"
@@ -34,6 +36,11 @@
 
 namespace android::inputdispatcher {
 
+template <typename T>
+struct SpHash {
+    size_t operator()(const sp<T>& k) const { return std::hash<T*>()(k.get()); }
+};
+
 sp<IBinder> FocusResolver::getFocusedWindowToken(int32_t displayId) const {
     auto it = mFocusedWindowTokenByDisplay.find(displayId);
     return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr;
@@ -54,30 +61,30 @@
         int32_t displayId, const std::vector<sp<WindowInfoHandle>>& windows) {
     std::string removeFocusReason;
 
-    // Check if the currently focused window is still focusable.
-    const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
-    if (currentFocus) {
-        Focusability result = isTokenFocusable(currentFocus, windows);
-        if (result == Focusability::OK) {
-            return std::nullopt;
-        }
-        removeFocusReason = ftl::enum_string(result);
-    }
-
-    // We don't have a focused window or the currently focused window is no longer focusable. Check
-    // to see if we can grant focus to the window that previously requested focus.
     const std::optional<FocusRequest> request = getFocusRequest(displayId);
+    const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
+
+    // Find the next focused token based on the latest FocusRequest. If the requested focus window
+    // cannot be focused, focus will be removed.
     if (request) {
         sp<IBinder> requestedFocus = request->token;
-        const Focusability result = isTokenFocusable(requestedFocus, windows);
+        sp<WindowInfoHandle> resolvedFocusWindow;
+        Focusability result = getResolvedFocusWindow(requestedFocus, windows, resolvedFocusWindow);
+        if (result == Focusability::OK && resolvedFocusWindow->getToken() == currentFocus) {
+            return std::nullopt;
+        }
         const Focusability previousResult = mLastFocusResultByDisplay[displayId];
         mLastFocusResultByDisplay[displayId] = result;
         if (result == Focusability::OK) {
+            LOG_ALWAYS_FATAL_IF(!resolvedFocusWindow,
+                                "Focused window should be non-null when result is OK!");
             return updateFocusedWindow(displayId,
                                        "Window became focusable. Previous reason: " +
                                                ftl::enum_string(previousResult),
-                                       requestedFocus, request->windowName);
+                                       resolvedFocusWindow->getToken(),
+                                       resolvedFocusWindow->getName());
         }
+        removeFocusReason = ftl::enum_string(result);
     }
 
     // Focused window is no longer focusable and we don't have a suitable focus request to grant.
@@ -96,35 +103,18 @@
         return std::nullopt;
     }
 
-    // Handle conditional focus requests, i.e. requests that have a focused token. These requests
-    // are not persistent. If the window is no longer focusable, we expect focus to go back to the
-    // previously focused window.
-    if (request.focusedToken) {
-        if (currentFocus != request.focusedToken) {
-            ALOGW("setFocusedWindow %s on display %" PRId32
-                  " ignored, reason: focusedToken %s is not focused",
-                  request.windowName.c_str(), displayId, request.focusedWindowName.c_str());
-            return std::nullopt;
-        }
-        Focusability result = isTokenFocusable(request.token, windows);
-        if (result == Focusability::OK) {
-            return updateFocusedWindow(displayId, "setFocusedWindow with focus check",
-                                       request.token, request.windowName);
-        }
-        ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason: %s",
-              request.windowName.c_str(), displayId, ftl::enum_string(result).c_str());
-        return std::nullopt;
-    }
-
-    Focusability result = isTokenFocusable(request.token, windows);
+    sp<WindowInfoHandle> resolvedFocusWindow;
+    Focusability result = getResolvedFocusWindow(request.token, windows, resolvedFocusWindow);
     // Update focus request. The focus resolver will always try to handle this request if there is
     // no focused window on the display.
     mFocusRequestByDisplay[displayId] = request;
     mLastFocusResultByDisplay[displayId] = result;
 
     if (result == Focusability::OK) {
-        return updateFocusedWindow(displayId, "setFocusedWindow", request.token,
-                                   request.windowName);
+        LOG_ALWAYS_FATAL_IF(!resolvedFocusWindow,
+                            "Focused window should be non-null when result is OK!");
+        return updateFocusedWindow(displayId, "setFocusedWindow", resolvedFocusWindow->getToken(),
+                                   resolvedFocusWindow->getName());
     }
 
     // The requested window is not currently focusable. Wait for the window to become focusable
@@ -134,11 +124,43 @@
                                nullptr);
 }
 
+FocusResolver::Focusability FocusResolver::getResolvedFocusWindow(
+        const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows,
+        sp<WindowInfoHandle>& outFocusableWindow) {
+    sp<IBinder> curFocusCandidate = token;
+    bool focusedWindowFound = false;
+
+    // Keep track of all windows reached to prevent a cyclical transferFocus request.
+    std::unordered_set<sp<IBinder>, SpHash<IBinder>> tokensReached;
+
+    while (curFocusCandidate != nullptr && tokensReached.count(curFocusCandidate) == 0) {
+        tokensReached.emplace(curFocusCandidate);
+        Focusability result = isTokenFocusable(curFocusCandidate, windows, outFocusableWindow);
+        if (result == Focusability::OK) {
+            LOG_ALWAYS_FATAL_IF(!outFocusableWindow,
+                                "Focused window should be non-null when result is OK!");
+            focusedWindowFound = true;
+            // outFocusableWindow has been updated by isTokenFocusable to contain
+            // the window info for curFocusCandidate. See if we can grant focus
+            // to the token that it wants to transfer its focus to.
+            curFocusCandidate = outFocusableWindow->getInfo()->focusTransferTarget;
+        }
+
+        // If the initial token is not focusable, return early with the failed result.
+        if (!focusedWindowFound) {
+            return result;
+        }
+    }
+
+    return focusedWindowFound ? Focusability::OK : Focusability::NO_WINDOW;
+}
+
 FocusResolver::Focusability FocusResolver::isTokenFocusable(
-        const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows) {
+        const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows,
+        sp<WindowInfoHandle>& outFocusableWindow) {
     bool allWindowsAreFocusable = true;
-    bool visibleWindowFound = false;
     bool windowFound = false;
+    sp<WindowInfoHandle> visibleWindowHandle = nullptr;
     for (const sp<WindowInfoHandle>& window : windows) {
         if (window->getToken() != token) {
             continue;
@@ -146,7 +168,7 @@
         windowFound = true;
         if (!window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)) {
             // Check if at least a single window is visible.
-            visibleWindowFound = true;
+            visibleWindowHandle = window;
         }
         if (window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE)) {
             // Check if all windows with the window token are focusable.
@@ -161,10 +183,12 @@
     if (!allWindowsAreFocusable) {
         return Focusability::NOT_FOCUSABLE;
     }
-    if (!visibleWindowFound) {
+    if (!visibleWindowHandle) {
         return Focusability::NOT_VISIBLE;
     }
 
+    // Only set the outFoundWindow if the window can be focused
+    outFocusableWindow = visibleWindowHandle;
     return Focusability::OK;
 }
 
diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h
index 6d11a77..5bb157b 100644
--- a/services/inputflinger/dispatcher/FocusResolver.h
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -92,7 +92,13 @@
     //
     static Focusability isTokenFocusable(
             const sp<IBinder>& token,
-            const std::vector<sp<android::gui::WindowInfoHandle>>& windows);
+            const std::vector<sp<android::gui::WindowInfoHandle>>& windows,
+            sp<android::gui::WindowInfoHandle>& outFocusableWindow);
+
+    static FocusResolver::Focusability getResolvedFocusWindow(
+            const sp<IBinder>& token,
+            const std::vector<sp<android::gui::WindowInfoHandle>>& windows,
+            sp<android::gui::WindowInfoHandle>& outFocusableWindow);
 
     // Focus tracking for keys, trackball, etc. A window token can be associated with one or
     // more InputWindowHandles. If a window is mirrored, the window and its mirror will share
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
index ccdb37a..5440a98 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -237,7 +237,60 @@
     ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
 }
 
-TEST(FocusResolverTest, ConditionalFocusRequestsAreNotPersistent) {
+TEST(FocusResolverTest, FocusTransferTarget) {
+    sp<IBinder> hostWindowToken = sp<BBinder>::make();
+    std::vector<sp<WindowInfoHandle>> windows;
+
+    sp<FakeWindowHandle> hostWindow =
+            sp<FakeWindowHandle>::make("Host Window", hostWindowToken, /*focusable=*/true,
+                                       /*visible=*/true);
+    windows.push_back(hostWindow);
+    sp<IBinder> embeddedWindowToken = sp<BBinder>::make();
+    sp<FakeWindowHandle> embeddedWindow =
+            sp<FakeWindowHandle>::make("Embedded Window", embeddedWindowToken, /*focusable=*/false,
+                                       /*visible=*/true);
+    windows.push_back(embeddedWindow);
+
+    FocusRequest request;
+    request.displayId = 42;
+    request.token = hostWindowToken;
+
+    // Host wants to transfer touch to embedded.
+    hostWindow->editInfo()->focusTransferTarget = embeddedWindowToken;
+
+    FocusResolver focusResolver;
+    std::optional<FocusResolver::FocusChanges> changes =
+            focusResolver.setFocusedWindow(request, windows);
+    // Embedded was not focusable so host gains focus.
+    ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ hostWindowToken);
+
+    // Embedded is now focusable so will gain focus
+    embeddedWindow->setFocusable(true);
+    changes = focusResolver.setInputWindows(request.displayId, windows);
+    ASSERT_FOCUS_CHANGE(changes, /*from*/ hostWindowToken, /*to*/ embeddedWindowToken);
+
+    // Embedded is not visible so host will get focus
+    embeddedWindow->setVisible(false);
+    changes = focusResolver.setInputWindows(request.displayId, windows);
+    ASSERT_FOCUS_CHANGE(changes, /*from*/ embeddedWindowToken, /*to*/ hostWindowToken);
+
+    // Embedded is now visible so will get focus
+    embeddedWindow->setVisible(true);
+    changes = focusResolver.setInputWindows(request.displayId, windows);
+    ASSERT_FOCUS_CHANGE(changes, /*from*/ hostWindowToken, /*to*/ embeddedWindowToken);
+
+    // Remove focusTransferTarget from host. Host will gain focus.
+    hostWindow->editInfo()->focusTransferTarget = nullptr;
+    changes = focusResolver.setInputWindows(request.displayId, windows);
+    ASSERT_FOCUS_CHANGE(changes, /*from*/ embeddedWindowToken, /*to*/ hostWindowToken);
+
+    // Set invalid token for focusTransferTarget. Host will remain focus
+    hostWindow->editInfo()->focusTransferTarget = sp<BBinder>::make();
+    changes = focusResolver.setInputWindows(request.displayId, windows);
+    ASSERT_FALSE(changes);
+}
+
+TEST(FocusResolverTest, FocusTransferMultipleInChain) {
     sp<IBinder> hostWindowToken = sp<BBinder>::make();
     std::vector<sp<WindowInfoHandle>> windows;
 
@@ -251,43 +304,60 @@
                                        /*visible=*/true);
     windows.push_back(embeddedWindow);
 
+    sp<IBinder> embeddedWindowToken2 = sp<BBinder>::make();
+    sp<FakeWindowHandle> embeddedWindow2 =
+            sp<FakeWindowHandle>::make("Embedded Window2", embeddedWindowToken2, /*focusable=*/true,
+                                       /*visible=*/true);
+    windows.push_back(embeddedWindow2);
+
     FocusRequest request;
     request.displayId = 42;
     request.token = hostWindowToken;
+
+    hostWindow->editInfo()->focusTransferTarget = embeddedWindowToken;
+    embeddedWindow->editInfo()->focusTransferTarget = embeddedWindowToken2;
+
     FocusResolver focusResolver;
     std::optional<FocusResolver::FocusChanges> changes =
             focusResolver.setFocusedWindow(request, windows);
-    ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ hostWindowToken);
-
-    request.focusedToken = hostWindow->getToken();
-    request.token = embeddedWindowToken;
-    changes = focusResolver.setFocusedWindow(request, windows);
-    ASSERT_FOCUS_CHANGE(changes, /*from*/ hostWindowToken, /*to*/ embeddedWindowToken);
-
-    embeddedWindow->setFocusable(false);
-    changes = focusResolver.setInputWindows(request.displayId, windows);
-    // The embedded window is no longer focusable, provide focus back to the original focused
-    // window.
-    ASSERT_FOCUS_CHANGE(changes, /*from*/ embeddedWindowToken, /*to*/ hostWindowToken);
-
-    embeddedWindow->setFocusable(true);
-    changes = focusResolver.setInputWindows(request.displayId, windows);
-    // The embedded window is focusable again, but we it cannot gain focus unless there is another
-    // focus request.
-    ASSERT_FALSE(changes);
-
-    embeddedWindow->setVisible(false);
-    changes = focusResolver.setFocusedWindow(request, windows);
-    // If the embedded window is not visible/focusable, then we do not grant it focus and the
-    // request is dropped.
-    ASSERT_FALSE(changes);
-
-    embeddedWindow->setVisible(true);
-    changes = focusResolver.setInputWindows(request.displayId, windows);
-    // If the embedded window becomes visble/focusable, nothing changes since the request has been
-    // dropped.
-    ASSERT_FALSE(changes);
+    ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ embeddedWindowToken2);
 }
+
+TEST(FocusResolverTest, FocusTransferTargetCycle) {
+    sp<IBinder> hostWindowToken = sp<BBinder>::make();
+    std::vector<sp<WindowInfoHandle>> windows;
+
+    sp<FakeWindowHandle> hostWindow =
+            sp<FakeWindowHandle>::make("Host Window", hostWindowToken, /*focusable=*/true,
+                                       /*visible=*/true);
+    windows.push_back(hostWindow);
+    sp<IBinder> embeddedWindowToken = sp<BBinder>::make();
+    sp<FakeWindowHandle> embeddedWindow =
+            sp<FakeWindowHandle>::make("Embedded Window", embeddedWindowToken, /*focusable=*/true,
+                                       /*visible=*/true);
+    windows.push_back(embeddedWindow);
+
+    sp<IBinder> embeddedWindowToken2 = sp<BBinder>::make();
+    sp<FakeWindowHandle> embeddedWindow2 =
+            sp<FakeWindowHandle>::make("Embedded Window2", embeddedWindowToken2, /*focusable=*/true,
+                                       /*visible=*/true);
+    windows.push_back(embeddedWindow2);
+
+    FocusRequest request;
+    request.displayId = 42;
+    request.token = hostWindowToken;
+
+    hostWindow->editInfo()->focusTransferTarget = embeddedWindowToken;
+    embeddedWindow->editInfo()->focusTransferTarget = embeddedWindowToken2;
+    embeddedWindow2->editInfo()->focusTransferTarget = hostWindowToken;
+
+    FocusResolver focusResolver;
+    std::optional<FocusResolver::FocusChanges> changes =
+            focusResolver.setFocusedWindow(request, windows);
+    // Cycle will be detected and stop right before trying to transfer token to host again.
+    ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ embeddedWindowToken2);
+}
+
 TEST(FocusResolverTest, FocusRequestsAreClearedWhenWindowIsRemoved) {
     sp<IBinder> windowToken = sp<BBinder>::make();
     std::vector<sp<WindowInfoHandle>> windows;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index a58ad84..fb808eb 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -637,14 +637,10 @@
         }
     }
 
-    void setFocusedWindow(const sp<WindowInfoHandle>& window,
-                          const sp<WindowInfoHandle>& focusedWindow = nullptr) {
+    void setFocusedWindow(const sp<WindowInfoHandle>& window) {
         FocusRequest request;
         request.token = window->getToken();
         request.windowName = window->getName();
-        if (focusedWindow) {
-            request.focusedToken = focusedWindow->getToken();
-        }
         request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
         request.displayId = window->getInfo()->displayId;
         mDispatcher->setFocusedWindow(request);
@@ -5229,7 +5225,8 @@
     setFocusedWindow(windowTop);
     windowTop->consumeFocusEvent(true);
 
-    setFocusedWindow(windowSecond, windowTop);
+    windowTop->editInfo()->focusTransferTarget = windowSecond->getToken();
+    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
     windowSecond->consumeFocusEvent(true);
     windowTop->consumeFocusEvent(false);
 
@@ -5240,7 +5237,7 @@
     windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
 }
 
-TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestFocusTokenNotFocused) {
+TEST_F(InputDispatcherTest, SetFocusedWindow_TransferFocusTokenNotFocusable) {
     std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
     sp<FakeWindowHandle> windowTop =
             sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
@@ -5249,15 +5246,17 @@
     mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
 
     windowTop->setFocusable(true);
-    windowSecond->setFocusable(true);
+    windowSecond->setFocusable(false);
+    windowTop->editInfo()->focusTransferTarget = windowSecond->getToken();
     mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {windowTop, windowSecond}}});
-    setFocusedWindow(windowSecond, windowTop);
+    setFocusedWindow(windowTop);
+    windowTop->consumeFocusEvent(true);
 
-    ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, injectKeyDown(mDispatcher))
-            << "Inject key event should return InputEventInjectionResult::TIMED_OUT";
+    ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher))
+            << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
 
     // Event should be dropped.
-    windowTop->assertNoEvents();
+    windowTop->consumeKeyDown(ADISPLAY_ID_NONE);
     windowSecond->assertNoEvents();
 }