Loading services/inputflinger/dispatcher/FocusResolver.cpp +83 −89 Original line number Diff line number Diff line Loading @@ -40,75 +40,56 @@ 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); if (currentFocus) { FocusResult result = isTokenFocusable(currentFocus, windows); if (result != FocusResult::OK) { return updateFocusedWindow(displayId, NamedEnum::string(result), nullptr); } } std::string removeFocusReason; // Check if any pending focus requests can be resolved. std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId); if (!pendingRequest) { // 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; } 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; removeFocusReason = NamedEnum::string(result); } 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); // 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); } 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", Loading @@ -116,34 +97,45 @@ std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow( 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); } 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()); return std::nullopt; } return std::nullopt; 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); } FocusResolver::FocusResult FocusResolver::isTokenFocusable( // 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::Focusability FocusResolver::isTokenFocusable( const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) { bool allWindowsAreFocusable = true; bool visibleWindowFound = false; Loading @@ -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( Loading Loading @@ -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; } Loading services/inputflinger/dispatcher/FocusResolver.h +21 −11 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -61,7 +68,7 @@ public: std::string dump() const; private: enum class FocusResult { enum class Focusability { OK, NO_WINDOW, NOT_FOCUSABLE, Loading @@ -77,7 +84,7 @@ 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, 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 Loading @@ -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 services/inputflinger/tests/FocusResolver_test.cpp +107 −12 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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 Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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) { Loading @@ -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 Loading
services/inputflinger/dispatcher/FocusResolver.cpp +83 −89 Original line number Diff line number Diff line Loading @@ -40,75 +40,56 @@ 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); if (currentFocus) { FocusResult result = isTokenFocusable(currentFocus, windows); if (result != FocusResult::OK) { return updateFocusedWindow(displayId, NamedEnum::string(result), nullptr); } } std::string removeFocusReason; // Check if any pending focus requests can be resolved. std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId); if (!pendingRequest) { // 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; } 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; removeFocusReason = NamedEnum::string(result); } 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); // 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); } 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", Loading @@ -116,34 +97,45 @@ std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow( 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); } 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()); return std::nullopt; } return std::nullopt; 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); } FocusResolver::FocusResult FocusResolver::isTokenFocusable( // 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::Focusability FocusResolver::isTokenFocusable( const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) { bool allWindowsAreFocusable = true; bool visibleWindowFound = false; Loading @@ -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( Loading Loading @@ -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; } Loading
services/inputflinger/dispatcher/FocusResolver.h +21 −11 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -61,7 +68,7 @@ public: std::string dump() const; private: enum class FocusResult { enum class Focusability { OK, NO_WINDOW, NOT_FOCUSABLE, Loading @@ -77,7 +84,7 @@ 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, 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 Loading @@ -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
services/inputflinger/tests/FocusResolver_test.cpp +107 −12 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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 Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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) { Loading @@ -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