summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/inputflinger/dispatcher/FocusResolver.cpp172
-rw-r--r--services/inputflinger/dispatcher/FocusResolver.h32
-rw-r--r--services/inputflinger/tests/FocusResolver_test.cpp119
3 files changed, 211 insertions, 112 deletions
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
index ee6b38b93f..2db8c13a67 100644
--- a/services/inputflinger/dispatcher/FocusResolver.cpp
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -40,110 +40,102 @@ sp<IBinder> FocusResolver::getFocusedWindowToken(int32_t displayId) const {
return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr;
}
-std::optional<FocusRequest> FocusResolver::getPendingRequest(int32_t displayId) {
- auto it = mPendingFocusRequests.find(displayId);
- return it != mPendingFocusRequests.end() ? std::make_optional<>(it->second) : std::nullopt;
+std::optional<FocusRequest> FocusResolver::getFocusRequest(int32_t displayId) {
+ auto it = mFocusRequestByDisplay.find(displayId);
+ return it != mFocusRequestByDisplay.end() ? std::make_optional<>(it->second) : std::nullopt;
}
+/**
+ * 'setInputWindows' is called when the window properties change. Here we will check whether the
+ * currently focused window can remain focused. If the currently focused window remains eligible
+ * for focus ('isTokenFocusable' returns OK), then we will continue to grant it focus otherwise
+ * we will check if the previous focus request is eligible to receive focus.
+ */
std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows) {
- // If the current focused window becomes unfocusable, remove focus.
- sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
+ std::string removeFocusReason;
+
+ // Check if the currently focused window is still focusable.
+ const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
if (currentFocus) {
- FocusResult result = isTokenFocusable(currentFocus, windows);
- if (result != FocusResult::OK) {
- return updateFocusedWindow(displayId, NamedEnum::string(result), nullptr);
+ Focusability result = isTokenFocusable(currentFocus, windows);
+ if (result == Focusability::OK) {
+ return std::nullopt;
+ }
+ removeFocusReason = NamedEnum::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);
+ if (request) {
+ sp<IBinder> requestedFocus = request->token;
+ const Focusability result = isTokenFocusable(requestedFocus, windows);
+ const Focusability previousResult = mLastFocusResultByDisplay[displayId];
+ mLastFocusResultByDisplay[displayId] = result;
+ if (result == Focusability::OK) {
+ return updateFocusedWindow(displayId,
+ "Window became focusable. Previous reason: " +
+ NamedEnum::string(previousResult),
+ requestedFocus, request->windowName);
}
}
- // Check if any pending focus requests can be resolved.
- std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
- if (!pendingRequest) {
- return std::nullopt;
- }
-
- sp<IBinder> requestedFocus = pendingRequest->token;
- std::string windowName = pendingRequest->windowName;
- if (currentFocus == requestedFocus) {
- ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
- windowName.c_str(), displayId);
- mPendingFocusRequests.erase(displayId);
- return std::nullopt;
- }
-
- FocusResult result = isTokenFocusable(requestedFocus, windows);
- // If the window from the pending request is now visible, provide it focus.
- if (result == FocusResult::OK) {
- mPendingFocusRequests.erase(displayId);
- return updateFocusedWindow(displayId, "Window became visible", requestedFocus, windowName);
- }
-
- if (result != FocusResult::NOT_VISIBLE) {
- // Drop the request if we are unable to change the focus for a reason other than visibility.
- ALOGW("Focus request %s on display %" PRId32 " ignored, reason:%s", windowName.c_str(),
- displayId, NamedEnum::string(result).c_str());
- mPendingFocusRequests.erase(displayId);
- }
- return std::nullopt;
+ // Focused window is no longer focusable and we don't have a suitable focus request to grant.
+ // Remove focus if needed.
+ return updateFocusedWindow(displayId, removeFocusReason, nullptr);
}
std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows) {
const int32_t displayId = request.displayId;
const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
- if (request.focusedToken && 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;
- }
-
- std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
- if (pendingRequest) {
- ALOGW("Pending focus request %s on display %" PRId32
- " ignored, reason:replaced by new request",
- pendingRequest->windowName.c_str(), displayId);
-
- // clear any pending focus requests
- mPendingFocusRequests.erase(displayId);
- }
-
if (currentFocus == request.token) {
ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow %s on display %" PRId32 " ignored, reason:already focused",
+ "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
request.windowName.c_str(), displayId);
return std::nullopt;
}
- FocusResult result = isTokenFocusable(request.token, windows);
- if (result == FocusResult::OK) {
- std::string reason =
- (request.focusedToken) ? "setFocusedWindow with focus check" : "setFocusedWindow";
- return updateFocusedWindow(displayId, reason, request.token, request.windowName);
+ // 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, NamedEnum::string(result).c_str());
+ return std::nullopt;
}
- if (result == FocusResult::NOT_VISIBLE) {
- // The requested window is not currently visible. Wait for the window to become visible
- // and then provide it focus. This is to handle situations where a user action triggers
- // a new window to appear. We want to be able to queue any key events after the user
- // action and deliver it to the newly focused window. In order for this to happen, we
- // take focus from the currently focused window so key events can be queued.
- ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow %s on display %" PRId32
- " pending, reason: window is not visible",
- request.windowName.c_str(), displayId);
- mPendingFocusRequests[displayId] = request;
- return updateFocusedWindow(displayId, "Waiting for window to be visible", nullptr);
- } else {
- ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason:%s",
- request.windowName.c_str(), displayId, NamedEnum::string(result).c_str());
+ Focusability result = isTokenFocusable(request.token, windows);
+ // 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);
}
- return std::nullopt;
+ // The requested window is not currently focusable. Wait for the window to become focusable
+ // but remove focus from the current window so that input events can go into a pending queue
+ // and be sent to the window when it becomes focused.
+ return updateFocusedWindow(displayId, "Waiting for window because " + NamedEnum::string(result),
+ nullptr);
}
-FocusResolver::FocusResult FocusResolver::isTokenFocusable(
+FocusResolver::Focusability FocusResolver::isTokenFocusable(
const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) {
bool allWindowsAreFocusable = true;
bool visibleWindowFound = false;
@@ -165,16 +157,16 @@ FocusResolver::FocusResult FocusResolver::isTokenFocusable(
}
if (!windowFound) {
- return FocusResult::NO_WINDOW;
+ return Focusability::NO_WINDOW;
}
if (!allWindowsAreFocusable) {
- return FocusResult::NOT_FOCUSABLE;
+ return Focusability::NOT_FOCUSABLE;
}
if (!visibleWindowFound) {
- return FocusResult::NOT_VISIBLE;
+ return Focusability::NOT_VISIBLE;
}
- return FocusResult::OK;
+ return Focusability::OK;
}
std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
@@ -209,15 +201,17 @@ std::string FocusResolver::dumpFocusedWindows() const {
std::string FocusResolver::dump() const {
std::string dump = dumpFocusedWindows();
-
- if (mPendingFocusRequests.empty()) {
- return dump + INDENT "PendingFocusRequests: <none>\n";
+ if (mFocusRequestByDisplay.empty()) {
+ return dump + INDENT "FocusRequests: <none>\n";
}
- dump += INDENT "PendingFocusRequests:\n";
- for (const auto& [displayId, request] : mPendingFocusRequests) {
- dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
- request.windowName.c_str());
+ dump += INDENT "FocusRequests:\n";
+ for (const auto& [displayId, request] : mFocusRequestByDisplay) {
+ auto it = mLastFocusResultByDisplay.find(displayId);
+ std::string result =
+ it != mLastFocusResultByDisplay.end() ? NamedEnum::string(it->second) : "";
+ dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s' result='%s'\n",
+ displayId, request.windowName.c_str(), result.c_str());
}
return dump;
}
diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h
index e067ad9f37..dc5eeebb4e 100644
--- a/services/inputflinger/dispatcher/FocusResolver.h
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -34,11 +34,18 @@ namespace android::inputdispatcher {
// is visible with the same token and all window handles with the same token are focusable.
// See FocusResolver::isTokenFocusable
//
-// Focus request - Request will be granted if the window is focusable. If the window is not
-// visible, then the request is kept in a pending state and granted when it becomes visible.
-// If window becomes not focusable, or another request comes in, the pending request is dropped.
+// Focus request - Request will be granted if the window is focusable. If it's not
+// focusable, then the request is persisted and granted when it becomes focusable. The currently
+// focused window will lose focus and any pending keys will be added to a queue so it can be sent
+// to the window when it gets focus.
+//
+// Condition focus request - Request with a focus token specified. Request will be granted if the
+// window is focusable and the focus token is the currently focused. Otherwise, the request is
+// dropped. Conditional focus requests are not persisted. The window will lose focus and go back
+// to the focus token if it becomes not focusable.
//
// Window handle updates - Focus is lost when the currently focused window becomes not focusable.
+// If the previous focus request is focusable, then we will try to grant that window focus.
class FocusResolver {
public:
// Returns the focused window token on the specified display.
@@ -61,7 +68,7 @@ public:
std::string dump() const;
private:
- enum class FocusResult {
+ enum class Focusability {
OK,
NO_WINDOW,
NOT_FOCUSABLE,
@@ -77,8 +84,8 @@ private:
// we expect the focusability of the windows to match since its hard to reason why one window
// can receive focus events and the other cannot when both are backed by the same input channel.
//
- static FocusResult isTokenFocusable(const sp<IBinder>& token,
- const std::vector<sp<InputWindowHandle>>& windows);
+ static Focusability isTokenFocusable(const sp<IBinder>& token,
+ const std::vector<sp<InputWindowHandle>>& windows);
// 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
@@ -87,15 +94,18 @@ private:
typedef std::pair<std::string /* name */, sp<IBinder>> NamedToken;
std::unordered_map<int32_t /* displayId */, NamedToken> mFocusedWindowTokenByDisplay;
- // This map will store a single pending focus request per display that cannot be currently
- // processed. This can happen if the window requested to be focused is not currently visible.
- // Such a window might become visible later, and these requests would be processed at that time.
- std::unordered_map<int32_t /* displayId */, FocusRequest> mPendingFocusRequests;
+ // This map will store the focus request per display. When the input window handles are updated,
+ // the current request will be checked to see if it can be processed at that time.
+ std::unordered_map<int32_t /* displayId */, FocusRequest> mFocusRequestByDisplay;
+
+ // Last reason for not granting a focus request. This is used to add more debug information
+ // in the event logs.
+ std::unordered_map<int32_t /* displayId */, Focusability> mLastFocusResultByDisplay;
std::optional<FocusResolver::FocusChanges> updateFocusedWindow(
int32_t displayId, const std::string& reason, const sp<IBinder>& token,
const std::string& tokenName = "");
- std::optional<FocusRequest> getPendingRequest(int32_t displayId);
+ std::optional<FocusRequest> getFocusRequest(int32_t displayId);
};
} // namespace android::inputdispatcher \ No newline at end of file
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
index ef3dd65d46..17efb5b99f 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -18,6 +18,12 @@
#include "../FocusResolver.h"
+#define ASSERT_FOCUS_CHANGE(_changes, _oldFocus, _newFocus) \
+ { \
+ ASSERT_EQ(_oldFocus, _changes->oldFocus); \
+ ASSERT_EQ(_newFocus, _changes->newFocus); \
+ }
+
// atest inputflinger_tests:FocusResolverTest
namespace android::inputdispatcher {
@@ -56,8 +62,7 @@ TEST(FocusResolverTest, SetFocusedWindow) {
FocusResolver focusResolver;
std::optional<FocusResolver::FocusChanges> changes =
focusResolver.setFocusedWindow(request, windows);
- ASSERT_EQ(nullptr, changes->oldFocus);
- ASSERT_EQ(focusableWindowToken, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
ASSERT_EQ(request.displayId, changes->displayId);
// invisible window cannot get focused
@@ -65,6 +70,7 @@ TEST(FocusResolverTest, SetFocusedWindow) {
changes = focusResolver.setFocusedWindow(request, windows);
ASSERT_EQ(focusableWindowToken, changes->oldFocus);
ASSERT_EQ(nullptr, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
// unfocusableWindowToken window cannot get focused
request.token = unfocusableWindowToken;
@@ -99,19 +105,17 @@ TEST(FocusResolverTest, SetFocusedMirroredWindow) {
FocusResolver focusResolver;
std::optional<FocusResolver::FocusChanges> changes =
focusResolver.setFocusedWindow(request, windows);
- ASSERT_EQ(nullptr, changes->oldFocus);
- ASSERT_EQ(focusableWindowToken, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
// mirrored window with one visible window can get focused
request.token = invisibleWindowToken;
changes = focusResolver.setFocusedWindow(request, windows);
- ASSERT_EQ(focusableWindowToken, changes->oldFocus);
- ASSERT_EQ(invisibleWindowToken, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ invisibleWindowToken);
// mirrored window with one or more unfocusable window cannot get focused
request.token = unfocusableWindowToken;
changes = focusResolver.setFocusedWindow(request, windows);
- ASSERT_FALSE(changes);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ invisibleWindowToken, /*to*/ nullptr);
}
TEST(FocusResolverTest, SetInputWindows) {
@@ -130,11 +134,10 @@ TEST(FocusResolverTest, SetInputWindows) {
focusResolver.setFocusedWindow(request, windows);
ASSERT_EQ(focusableWindowToken, changes->newFocus);
- // Window visibility changes and the window loses focused
+ // Window visibility changes and the window loses focus
window->setVisible(false);
changes = focusResolver.setInputWindows(request.displayId, windows);
- ASSERT_EQ(nullptr, changes->newFocus);
- ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
}
TEST(FocusResolverTest, FocusRequestsCanBePending) {
@@ -158,8 +161,100 @@ TEST(FocusResolverTest, FocusRequestsCanBePending) {
// Window visibility changes and the window gets focused
invisibleWindow->setVisible(true);
changes = focusResolver.setInputWindows(request.displayId, windows);
- ASSERT_EQ(nullptr, changes->oldFocus);
- ASSERT_EQ(invisibleWindowToken, changes->newFocus);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ invisibleWindowToken);
+}
+
+TEST(FocusResolverTest, FocusRequestsArePersistent) {
+ sp<IBinder> windowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+
+ sp<FakeWindowHandle> window = new FakeWindowHandle("Test Window", windowToken,
+ false /* focusable */, true /* visible */);
+ windows.push_back(window);
+
+ // non-focusable window cannot get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = windowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FALSE(changes);
+
+ // Focusability changes and the window gets focused
+ window->setFocusable(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+
+ // Visibility changes and the window loses focus
+ window->setVisible(false);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
+
+ // Visibility changes and the window gets focused
+ window->setVisible(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+
+ // Window is gone and the window loses focus
+ changes = focusResolver.setInputWindows(request.displayId, {});
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
+
+ // Window returns and the window gains focus
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+}
+
+TEST(FocusResolverTest, ConditionalFocusRequestsAreNotPersistent) {
+ sp<IBinder> hostWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+
+ sp<FakeWindowHandle> hostWindow =
+ new FakeWindowHandle("Host Window", hostWindowToken, true /* focusable */,
+ true /* visible */);
+ windows.push_back(hostWindow);
+ sp<IBinder> embeddedWindowToken = new BBinder();
+ sp<FakeWindowHandle> embeddedWindow =
+ new FakeWindowHandle("Embedded Window", embeddedWindowToken, true /* focusable */,
+ true /* visible */);
+ windows.push_back(embeddedWindow);
+
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = hostWindowToken;
+ 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);
}
} // namespace android::inputdispatcher