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
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();
}