FocusResolver: Persist focus requests

When WM provides a focus request, the request is handled or
kept in a pending state until the window becomes visible. The request
is dropped in all other cases such as the window handle does not have
the focusable flag set. This might lead to cases where a focus request
is dropped as a result of state mismatch or synchronization issues
between wm and sf.

This fix makes the focus request persistent and better aligns with the
previous model where the focus bit was set as part of the window handle.
This means a request will be handled as long as the window is focusable
or replaced by another request. If the window toggles its focusability,
it will lose and gain focus.

Test: atest inputflinger_tests
Test: go/wm-smoke
Test: atest atest FlickerTests
Bug: 176451870
Change-Id: I790a4b69c15f595109118d49167cf9a450e67ec9
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
index ee6b38b..2db8c13 100644
--- a/services/inputflinger/dispatcher/FocusResolver.cpp
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -40,110 +40,102 @@
     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);
-    }
-
-    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",
+    // 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;
     }
 
-    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);
+    }
+
+    // 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 @@
     }
 
     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::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 e067ad9..dc5eeeb 100644
--- a/services/inputflinger/dispatcher/FocusResolver.h
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -34,11 +34,18 @@
 //   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 @@
     std::string dump() const;
 
 private:
-    enum class FocusResult {
+    enum class Focusability {
         OK,
         NO_WINDOW,
         NOT_FOCUSABLE,
@@ -77,8 +84,8 @@
     // 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 @@
     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 ef3dd65..17efb5b 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 @@
     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 @@
     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 @@
     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 @@
             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 @@
     // 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