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) {