From 8577ea8e34068bc0188074feafc8a0ac08294022 Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 1 Jun 2022 16:32:26 -0500 Subject: Convert to using inputConfig for clone info in WindowInfo Remove explicit isClone flag and add CLONE to InputConfig Test: Builds Bug: 230300971 Change-Id: I7c63fc87547ab759434662d9766dd96fe0e5d9b0 --- libs/gui/WindowInfo.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'libs/gui/WindowInfo.cpp') diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index 4e966d1393..804ce4fac0 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -76,7 +76,7 @@ bool WindowInfo::operator==(const WindowInfo& info) const { info.inputConfig == inputConfig && info.displayId == displayId && info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop && info.applicationInfo == applicationInfo && info.layoutParamsType == layoutParamsType && - info.layoutParamsFlags == layoutParamsFlags && info.isClone == isClone; + info.layoutParamsFlags == layoutParamsFlags; } status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { @@ -124,8 +124,7 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->write(touchableRegion) ?: parcel->writeBool(replaceTouchableRegionWithCrop) ?: parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?: - parcel->writeStrongBinder(windowToken) ?: - parcel->writeBool(isClone); + parcel->writeStrongBinder(windowToken); // clang-format on return status; } @@ -176,8 +175,7 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { parcel->read(touchableRegion) ?: parcel->readBool(&replaceTouchableRegionWithCrop) ?: parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?: - parcel->readNullableStrongBinder(&windowToken) ?: - parcel->readBool(&isClone); + parcel->readNullableStrongBinder(&windowToken); // clang-format on if (status != OK) { -- cgit v1.2.3-59-g8ed1b From 847e85100dfa25f8b6911b6a2409f460b12c4bfc Mon Sep 17 00:00:00 2001 From: Chavi Weingarten Date: Wed, 29 Mar 2023 00:26:09 +0000 Subject: Add focusTransferTarget in WindowInfo A transfer focus request was originally added on FocusRequest object. This meant when a host wanted to provide focus to an embedded window, it would request to transfer focus, but provide its own token as the focusedToken in the request. In FocusResolver, it would ensure that the focusedToken was current focus before allow the request to proceed. This worked in some cases, but created races where clients had to understand timing in order to properly transfer focus. The new code creates a persistent focusTransferTarget value on the WindowInfo object. The host will add the embedded token as part of its own WindowInfo#focusTransferTarget. When FocusResolver determines that a window should gain focus, it will check to see if there is any focusTransferTarget value set. If there is, it will attempt to give focus to that window, if possible. Otherwise, it will fallback to the previous window in the chain. This solves a few issues with embedded windows. 1. Apps can request focus to the embedded by requesting focus to the SV that hosts the embedded. However, if they request focus too early, it will drop the request since the host has not yet gained focus. With the current code, it will transfer focus to the embedded once the host could have gained focus. If the host wants to revoke, the focusTransferTarget can be set to null, which will give the host focus again. 2. This fixes the issue if another window becomes focus. When WM gives focus back to the host window, it should automatically give focus to the embedded if it was last requested. The app shouldn't be required to maintain the last state of the embedded focus to see if it needs to transfer it again. It's actually not even possible once focus can be given to embedded via touch since only WM and Input know about this. By adding focusTransferTarget to WindowInfo, the focusedToken value in FocusRequest can be removed, as well. Test: InputDispatcher_test Test: FocusResolver_test Test: WindowInfo_test Bug: 230340812 Change-Id: I252403184f7060c37c82353638ac6ee25a0979fc --- libs/gui/WindowInfo.cpp | 5 +- libs/gui/android/gui/FocusRequest.aidl | 9 -- libs/gui/include/gui/WindowInfo.h | 5 + libs/gui/tests/EndToEndNativeInputTest.cpp | 2 - libs/gui/tests/WindowInfo_test.cpp | 2 + services/inputflinger/dispatcher/FocusResolver.cpp | 104 +++++++++++-------- services/inputflinger/dispatcher/FocusResolver.h | 8 +- services/inputflinger/tests/FocusResolver_test.cpp | 110 +++++++++++++++++---- .../inputflinger/tests/InputDispatcher_test.cpp | 23 +++-- 9 files changed, 183 insertions(+), 85 deletions(-) (limited to 'libs/gui/WindowInfo.cpp') diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index 804ce4fac0..6df9ff1664 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -125,6 +125,7 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->writeBool(replaceTouchableRegionWithCrop) ?: parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?: parcel->writeStrongBinder(windowToken); + parcel->writeStrongBinder(focusTransferTarget); // clang-format on return status; } @@ -175,7 +176,9 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { 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 b13c60049c..62d1b68147 100644 --- a/libs/gui/android/gui/FocusRequest.aidl +++ b/libs/gui/android/gui/FocusRequest.aidl @@ -23,15 +23,6 @@ parcelable FocusRequest { */ @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 diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index b01a3db52d..70b2ee8e32 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -236,6 +236,11 @@ struct WindowInfo : public Parcelable { Type layoutParamsType = Type::UNKNOWN; ftl::Flags 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 focusTransferTarget; + void setInputConfig(ftl::Flags config, bool value); void addTouchableRegion(const Rect& region); diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 5f80c16f61..9e8c65c678 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -272,8 +272,6 @@ public: 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 c51b244c50..11b87efda7 100644 --- a/libs/gui/tests/WindowInfo_test.cpp +++ b/libs/gui/tests/WindowInfo_test.cpp @@ -71,6 +71,7 @@ TEST(WindowInfo, Parcelling) { 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 @@ TEST(WindowInfo, Parcelling) { 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 4da846bcf8..0e4e79e88e 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 #define LOG_TAG "InputDispatcher" #define ATRACE_TAG ATRACE_TAG_INPUT @@ -25,6 +26,7 @@ #include #include #include +#include #include "DebugConfig.h" #include "FocusResolver.h" @@ -34,6 +36,11 @@ using android::gui::WindowInfoHandle; namespace android::inputdispatcher { +template +struct SpHash { + size_t operator()(const sp& k) const { return std::hash()(k.get()); } +}; + sp FocusResolver::getFocusedWindowToken(int32_t displayId) const { auto it = mFocusedWindowTokenByDisplay.find(displayId); return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr; @@ -54,30 +61,30 @@ std::optional FocusResolver::setInputWindows( int32_t displayId, const std::vector>& windows) { std::string removeFocusReason; - // Check if the currently focused window is still focusable. + const std::optional request = getFocusRequest(displayId); const sp 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 request = getFocusRequest(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 requestedFocus = request->token; - const Focusability result = isTokenFocusable(requestedFocus, windows); + sp 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 @@ std::optional FocusResolver::setFocusedWindow( 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 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 @@ std::optional FocusResolver::setFocusedWindow( nullptr); } +FocusResolver::Focusability FocusResolver::getResolvedFocusWindow( + const sp& token, const std::vector>& windows, + sp& outFocusableWindow) { + sp curFocusCandidate = token; + bool focusedWindowFound = false; + + // Keep track of all windows reached to prevent a cyclical transferFocus request. + std::unordered_set, SpHash> 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& token, const std::vector>& windows) { + const sp& token, const std::vector>& windows, + sp& outFocusableWindow) { bool allWindowsAreFocusable = true; - bool visibleWindowFound = false; bool windowFound = false; + sp visibleWindowHandle = nullptr; for (const sp& window : windows) { if (window->getToken() != token) { continue; @@ -146,7 +168,7 @@ FocusResolver::Focusability FocusResolver::isTokenFocusable( 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 @@ FocusResolver::Focusability FocusResolver::isTokenFocusable( 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 6d11a77aad..5bb157b7c6 100644 --- a/services/inputflinger/dispatcher/FocusResolver.h +++ b/services/inputflinger/dispatcher/FocusResolver.h @@ -92,7 +92,13 @@ private: // static Focusability isTokenFocusable( const sp& token, - const std::vector>& windows); + const std::vector>& windows, + sp& outFocusableWindow); + + static FocusResolver::Focusability getResolvedFocusWindow( + const sp& token, + const std::vector>& windows, + sp& 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 ccdb37afb0..5440a98db6 100644 --- a/services/inputflinger/tests/FocusResolver_test.cpp +++ b/services/inputflinger/tests/FocusResolver_test.cpp @@ -237,7 +237,7 @@ TEST(FocusResolverTest, FocusRequestsArePersistent) { ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken); } -TEST(FocusResolverTest, ConditionalFocusRequestsAreNotPersistent) { +TEST(FocusResolverTest, FocusTransferTarget) { sp hostWindowToken = sp::make(); std::vector> windows; @@ -247,47 +247,117 @@ TEST(FocusResolverTest, ConditionalFocusRequestsAreNotPersistent) { windows.push_back(hostWindow); sp embeddedWindowToken = sp::make(); sp embeddedWindow = - sp::make("Embedded Window", embeddedWindowToken, /*focusable=*/true, + sp::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 changes = focusResolver.setFocusedWindow(request, windows); + // Embedded was not focusable so host gains focus. ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ hostWindowToken); - request.focusedToken = hostWindow->getToken(); - request.token = embeddedWindowToken; - changes = focusResolver.setFocusedWindow(request, windows); + // 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); - embeddedWindow->setFocusable(false); + // Embedded is not visible so host will get focus + embeddedWindow->setVisible(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); + // Embedded is now visible so will get focus + embeddedWindow->setVisible(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); + ASSERT_FOCUS_CHANGE(changes, /*from*/ hostWindowToken, /*to*/ embeddedWindowToken); - 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); + // 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); - embeddedWindow->setVisible(true); + // Set invalid token for focusTransferTarget. Host will remain focus + hostWindow->editInfo()->focusTransferTarget = sp::make(); changes = focusResolver.setInputWindows(request.displayId, windows); - // If the embedded window becomes visble/focusable, nothing changes since the request has been - // dropped. ASSERT_FALSE(changes); } + +TEST(FocusResolverTest, FocusTransferMultipleInChain) { + sp hostWindowToken = sp::make(); + std::vector> windows; + + sp hostWindow = + sp::make("Host Window", hostWindowToken, /*focusable=*/true, + /*visible=*/true); + windows.push_back(hostWindow); + sp embeddedWindowToken = sp::make(); + sp embeddedWindow = + sp::make("Embedded Window", embeddedWindowToken, /*focusable=*/true, + /*visible=*/true); + windows.push_back(embeddedWindow); + + sp embeddedWindowToken2 = sp::make(); + sp embeddedWindow2 = + sp::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 changes = + focusResolver.setFocusedWindow(request, windows); + ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ embeddedWindowToken2); +} + +TEST(FocusResolverTest, FocusTransferTargetCycle) { + sp hostWindowToken = sp::make(); + std::vector> windows; + + sp hostWindow = + sp::make("Host Window", hostWindowToken, /*focusable=*/true, + /*visible=*/true); + windows.push_back(hostWindow); + sp embeddedWindowToken = sp::make(); + sp embeddedWindow = + sp::make("Embedded Window", embeddedWindowToken, /*focusable=*/true, + /*visible=*/true); + windows.push_back(embeddedWindow); + + sp embeddedWindowToken2 = sp::make(); + sp embeddedWindow2 = + sp::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 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 windowToken = sp::make(); std::vector> windows; diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index a58ad84e90..fb808eb2a3 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -637,14 +637,10 @@ protected: } } - void setFocusedWindow(const sp& window, - const sp& focusedWindow = nullptr) { + void setFocusedWindow(const sp& 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 @@ TEST_F(InputDispatcherTest, SetFocusedWindow_CheckFocusedToken) { 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 @@ TEST_F(InputDispatcherTest, SetFocusedWindow_CheckFocusedToken) { windowSecond->consumeKeyDown(ADISPLAY_ID_NONE); } -TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestFocusTokenNotFocused) { +TEST_F(InputDispatcherTest, SetFocusedWindow_TransferFocusTokenNotFocusable) { std::shared_ptr application = std::make_shared(); sp windowTop = sp::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); @@ -5249,15 +5246,17 @@ TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestFocusTokenNotFocused) { 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(); } -- cgit v1.2.3-59-g8ed1b