InputDispatcher: Refactor focus logic into FocusResolver

Pull out logic that keeps track of the focused window per display and
the logic used to determine focus changes to a new class. Input
Dispatcher will feed in focus requests & window handle changes. It
will then consume any focus changes.

There is no functional changes in this cl. Additional logging has been
added when we drop a focus request.

Test: atest inputflinger_tests

Change-Id: I04c0c38907cca5d6d98405a6a9f7eb7c497fd90f
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index d467692..e5b131e 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -25,6 +25,7 @@
         "AnrTracker.cpp",
         "Connection.cpp",
         "Entry.cpp",
+        "FocusResolver.cpp",
         "InjectionState.cpp",
         "InputDispatcher.cpp",
         "InputDispatcherFactory.cpp",
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 6953d04..a19b04f 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -102,7 +102,7 @@
 
 // Focus notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries
 FocusEntry::FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
-                       std::string_view reason)
+                       const std::string& reason)
       : EventEntry(id, Type::FOCUS, eventTime, POLICY_FLAG_PASS_TO_USER),
         connectionToken(connectionToken),
         hasFocus(hasFocus),
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 26b641d..499f42e 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -93,10 +93,10 @@
 struct FocusEntry : EventEntry {
     sp<IBinder> connectionToken;
     bool hasFocus;
-    std::string_view reason;
+    std::string reason;
 
     FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
-               std::string_view reason);
+               const std::string& reason);
     std::string getDescription() const override;
 
     virtual ~FocusEntry();
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
new file mode 100644
index 0000000..ee6b38b
--- /dev/null
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "FocusResolver"
+#define ATRACE_TAG ATRACE_TAG_INPUT
+
+#define INDENT "  "
+#define INDENT2 "    "
+
+// Log debug messages about input focus tracking.
+static constexpr bool DEBUG_FOCUS = false;
+
+#include <inttypes.h>
+
+#include <android-base/stringprintf.h>
+#include <binder/Binder.h>
+#include <input/InputWindow.h>
+#include <input/NamedEnum.h>
+#include <log/log.h>
+
+#include "FocusResolver.h"
+
+namespace android::inputdispatcher {
+
+sp<IBinder> FocusResolver::getFocusedWindowToken(int32_t displayId) const {
+    auto it = mFocusedWindowTokenByDisplay.find(displayId);
+    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<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);
+        }
+    }
+
+    // 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;
+}
+
+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",
+                 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",
+              request.windowName.c_str(), displayId, NamedEnum::string(result).c_str());
+    }
+
+    return std::nullopt;
+}
+
+FocusResolver::FocusResult FocusResolver::isTokenFocusable(
+        const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) {
+    bool allWindowsAreFocusable = true;
+    bool visibleWindowFound = false;
+    bool windowFound = false;
+    for (const sp<InputWindowHandle>& window : windows) {
+        if (window->getToken() != token) {
+            continue;
+        }
+        windowFound = true;
+        if (window->getInfo()->visible) {
+            // Check if at least a single window is visible.
+            visibleWindowFound = true;
+        }
+        if (!window->getInfo()->focusable) {
+            // Check if all windows with the window token are focusable.
+            allWindowsAreFocusable = false;
+            break;
+        }
+    }
+
+    if (!windowFound) {
+        return FocusResult::NO_WINDOW;
+    }
+    if (!allWindowsAreFocusable) {
+        return FocusResult::NOT_FOCUSABLE;
+    }
+    if (!visibleWindowFound) {
+        return FocusResult::NOT_VISIBLE;
+    }
+
+    return FocusResult::OK;
+}
+
+std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
+        int32_t displayId, const std::string& reason, const sp<IBinder>& newFocus,
+        const std::string& tokenName) {
+    sp<IBinder> oldFocus = getFocusedWindowToken(displayId);
+    if (newFocus == oldFocus) {
+        return std::nullopt;
+    }
+    if (newFocus) {
+        mFocusedWindowTokenByDisplay[displayId] = {tokenName, newFocus};
+    } else {
+        mFocusedWindowTokenByDisplay.erase(displayId);
+    }
+
+    return {{oldFocus, newFocus, displayId, reason}};
+}
+
+std::string FocusResolver::dumpFocusedWindows() const {
+    if (mFocusedWindowTokenByDisplay.empty()) {
+        return INDENT "FocusedWindows: <none>\n";
+    }
+
+    std::string dump;
+    dump += INDENT "FocusedWindows:\n";
+    for (const auto& [displayId, namedToken] : mFocusedWindowTokenByDisplay) {
+        dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
+                                   namedToken.first.c_str());
+    }
+    return dump;
+}
+
+std::string FocusResolver::dump() const {
+    std::string dump = dumpFocusedWindows();
+
+    if (mPendingFocusRequests.empty()) {
+        return dump + INDENT "PendingFocusRequests: <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());
+    }
+    return dump;
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h
new file mode 100644
index 0000000..e067ad9
--- /dev/null
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <optional>
+#include <unordered_map>
+
+#include <android/FocusRequest.h>
+#include <binder/Binder.h>
+#include <input/InputWindow.h>
+
+namespace android::inputdispatcher {
+
+// Keeps track of the focused window per display. The class listens to updates from input dispatcher
+// and provides focus changes.
+//
+// Focus Policy
+//   Window focusabilty - A window token can be focused if there is at least one window handle that
+//   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.
+//
+//   Window handle updates - Focus is lost when the currently focused window becomes not focusable.
+class FocusResolver {
+public:
+    // Returns the focused window token on the specified display.
+    sp<IBinder> getFocusedWindowToken(int32_t displayId) const;
+
+    struct FocusChanges {
+        sp<IBinder> oldFocus;
+        sp<IBinder> newFocus;
+        int32_t displayId;
+        std::string reason;
+    };
+    std::optional<FocusResolver::FocusChanges> setInputWindows(
+            int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows);
+    std::optional<FocusResolver::FocusChanges> setFocusedWindow(
+            const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows);
+
+    // exposed for debugging
+    bool hasFocusedWindowTokens() const { return !mFocusedWindowTokenByDisplay.empty(); }
+    std::string dumpFocusedWindows() const;
+    std::string dump() const;
+
+private:
+    enum class FocusResult {
+        OK,
+        NO_WINDOW,
+        NOT_FOCUSABLE,
+        NOT_VISIBLE,
+    };
+
+    // Checks if the window token can be focused on a display. The token can be focused if there is
+    // at least one window handle that is visible with the same token and all window handles with
+    // the same token are focusable.
+    //
+    // In the case of mirroring, two windows may share the same window token and their visibility
+    // might be different. Example, the mirrored window can cover the window its mirroring. However,
+    // 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);
+
+    // 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
+    // the same token. Focus is tracked by the token per display and the events are dispatched
+    // to the channel associated by this token.
+    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;
+
+    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);
+};
+
+} // namespace android::inputdispatcher
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index b3558c6..d60bcd8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -293,15 +293,6 @@
     return removed;
 }
 
-/**
- * Find the entry in std::unordered_map by key and return the value as an optional.
- */
-template <typename K, typename V>
-static std::optional<V> getOptionalValueByKey(const std::unordered_map<K, V>& map, K key) {
-    auto it = map.find(key);
-    return it != map.end() ? std::optional<V>{it->second} : std::nullopt;
-}
-
 static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) {
     if (first == second) {
         return true;
@@ -420,19 +411,6 @@
     return result;
 }
 
-const char* InputDispatcher::typeToString(InputDispatcher::FocusResult result) {
-    switch (result) {
-        case InputDispatcher::FocusResult::OK:
-            return "Ok";
-        case InputDispatcher::FocusResult::NO_WINDOW:
-            return "Window not found";
-        case InputDispatcher::FocusResult::NOT_FOCUSABLE:
-            return "Window not focusable";
-        case InputDispatcher::FocusResult::NOT_VISIBLE:
-            return "Window not visible";
-    }
-}
-
 template <typename T>
 static bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) {
     if (lhs == nullptr && rhs == nullptr) {
@@ -1218,7 +1196,7 @@
 }
 
 void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
-                                              std::string_view reason) {
+                                              const std::string& reason) {
     if (mPendingEvent != nullptr) {
         // Move the pending event to the front of the queue. This will give the chance
         // for the pending event to get dispatched to the newly focused window
@@ -1281,7 +1259,7 @@
             ALOGW("No window requested Pointer Capture.");
             return;
         }
-        token = getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+        token = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
         LOG_ALWAYS_FATAL_IF(!token, "Cannot find focused window for Pointer Capture.");
         mWindowTokenWithPointerCapture = token;
     } else {
@@ -1380,7 +1358,7 @@
             std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
                     &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
             sp<IBinder> focusedWindowToken =
-                    getValueByKey(mFocusedWindowTokenByDisplay, getTargetDisplayId(*entry));
+                    mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry));
             commandEntry->connectionToken = focusedWindowToken;
             commandEntry->keyEntry = entry;
             postCommandLocked(std::move(commandEntry));
@@ -2910,7 +2888,7 @@
         return;
     }
 
-    sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+    sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
     if (focusedToken == token) {
         // ignore since token is focused
         return;
@@ -4159,7 +4137,7 @@
 }
 
 sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
-    sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+    sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId);
     return getWindowHandleLocked(focusedToken, displayId);
 }
 
@@ -4323,24 +4301,10 @@
         mLastHoverWindowHandle = nullptr;
     }
 
-    sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
-    if (focusedToken) {
-        FocusResult result = checkTokenFocusableLocked(focusedToken, displayId);
-        if (result != FocusResult::OK) {
-            onFocusChangedLocked(focusedToken, nullptr, displayId, typeToString(result));
-        }
-    }
-
-    std::optional<FocusRequest> focusRequest =
-            getOptionalValueByKey(mPendingFocusRequests, displayId);
-    if (focusRequest) {
-        // If the window from the pending request is now visible, provide it focus.
-        FocusResult result = handleFocusRequestLocked(*focusRequest);
-        if (result != FocusResult::NOT_VISIBLE) {
-            // Drop the request if we were able to change the focus or we cannot change
-            // it for another reason.
-            mPendingFocusRequests.erase(displayId);
-        }
+    std::optional<FocusResolver::FocusChanges> changes =
+            mFocusResolver.setInputWindows(displayId, windowHandles);
+    if (changes) {
+        onFocusChangedLocked(*changes);
     }
 
     std::unordered_map<int32_t, TouchState>::iterator stateIt =
@@ -4444,7 +4408,7 @@
 
         if (mFocusedDisplayId != displayId) {
             sp<IBinder> oldFocusedWindowToken =
-                    getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+                    mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
             if (oldFocusedWindowToken != nullptr) {
                 std::shared_ptr<InputChannel> inputChannel =
                         getInputChannelLocked(oldFocusedWindowToken);
@@ -4459,15 +4423,14 @@
             mFocusedDisplayId = displayId;
 
             // Find new focused window and validate
-            sp<IBinder> newFocusedWindowToken =
-                    getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+            sp<IBinder> newFocusedWindowToken = mFocusResolver.getFocusedWindowToken(displayId);
             notifyFocusChangedLocked(oldFocusedWindowToken, newFocusedWindowToken);
 
             if (newFocusedWindowToken == nullptr) {
                 ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
-                if (!mFocusedWindowTokenByDisplay.empty()) {
+                if (mFocusResolver.hasFocusedWindowTokens()) {
                     ALOGE("But another display has a focused window\n%s",
-                          dumpFocusedWindowsLocked().c_str());
+                          mFocusResolver.dumpFocusedWindows().c_str());
                 }
             }
         }
@@ -4667,46 +4630,6 @@
     }
 }
 
-std::string InputDispatcher::dumpFocusedWindowsLocked() {
-    if (mFocusedWindowTokenByDisplay.empty()) {
-        return INDENT "FocusedWindows: <none>\n";
-    }
-
-    std::string dump;
-    dump += INDENT "FocusedWindows:\n";
-    for (auto& it : mFocusedWindowTokenByDisplay) {
-        const int32_t displayId = it.first;
-        const sp<InputWindowHandle> windowHandle = getFocusedWindowHandleLocked(displayId);
-        if (windowHandle) {
-            dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
-                                 windowHandle->getName().c_str());
-        } else {
-            dump += StringPrintf(INDENT2 "displayId=%" PRId32
-                                         " has focused token without a window'\n",
-                                 displayId);
-        }
-    }
-    return dump;
-}
-
-std::string InputDispatcher::dumpPendingFocusRequestsLocked() {
-    if (mPendingFocusRequests.empty()) {
-        return INDENT "mPendingFocusRequests: <none>\n";
-    }
-
-    std::string dump;
-    dump += INDENT "mPendingFocusRequests:\n";
-    for (const auto& [displayId, focusRequest] : mPendingFocusRequests) {
-        // Rather than printing raw values for focusRequest.token and focusRequest.focusedToken,
-        // try to resolve them to actual windows.
-        std::string windowName = getConnectionNameLocked(focusRequest.token);
-        std::string focusedWindowName = getConnectionNameLocked(focusRequest.focusedToken);
-        dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", token->%s, focusedToken->%s\n",
-                             displayId, windowName.c_str(), focusedWindowName.c_str());
-    }
-    return dump;
-}
-
 std::string InputDispatcher::dumpPointerCaptureStateLocked() {
     std::string dump;
 
@@ -4746,8 +4669,7 @@
         dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
     }
 
-    dump += dumpFocusedWindowsLocked();
-    dump += dumpPendingFocusRequestsLocked();
+    dump += mFocusResolver.dump();
     dump += dumpPointerCaptureStateLocked();
 
     if (!mTouchStatesByDisplay.empty()) {
@@ -5140,8 +5062,7 @@
                                           : "token without window");
         }
 
-        const sp<IBinder> focusedToken =
-                getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+        const sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
         if (focusedToken != windowToken) {
             ALOGW("Ignoring request to %s Pointer Capture: window does not have focus.",
                   enabled ? "enable" : "disable");
@@ -5885,81 +5806,28 @@
 void InputDispatcher::setFocusedWindow(const FocusRequest& request) {
     { // acquire lock
         std::scoped_lock _l(mLock);
-
-        const int32_t displayId = request.displayId;
-        const sp<IBinder> oldFocusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
-        if (request.focusedToken && oldFocusedToken != request.focusedToken) {
-            ALOGD_IF(DEBUG_FOCUS,
-                     "setFocusedWindow on display %" PRId32
-                     " ignored, reason: focusedToken is not focused",
-                     displayId);
-            return;
-        }
-
-        mPendingFocusRequests.erase(displayId);
-        FocusResult result = handleFocusRequestLocked(request);
-        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 on display %" PRId32
-                     " pending, reason: window is not visible",
-                     displayId);
-            mPendingFocusRequests[displayId] = request;
-            onFocusChangedLocked(oldFocusedToken, nullptr, displayId,
-                                 "setFocusedWindow_AwaitingWindowVisibility");
-        } else if (result != FocusResult::OK) {
-            ALOGW("setFocusedWindow on display %" PRId32 " ignored, reason:%s", displayId,
-                  typeToString(result));
+        std::optional<FocusResolver::FocusChanges> changes =
+                mFocusResolver.setFocusedWindow(request, getWindowHandlesLocked(request.displayId));
+        if (changes) {
+            onFocusChangedLocked(*changes);
         }
     } // release lock
     // Wake up poll loop since it may need to make new input dispatching choices.
     mLooper->wake();
 }
 
-InputDispatcher::FocusResult InputDispatcher::handleFocusRequestLocked(
-        const FocusRequest& request) {
-    const int32_t displayId = request.displayId;
-    const sp<IBinder> newFocusedToken = request.token;
-    const sp<IBinder> oldFocusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
-
-    if (oldFocusedToken == request.token) {
-        ALOGD_IF(DEBUG_FOCUS,
-                 "setFocusedWindow on display %" PRId32 " ignored, reason: already focused",
-                 displayId);
-        return FocusResult::OK;
-    }
-
-    FocusResult result = checkTokenFocusableLocked(newFocusedToken, displayId);
-    if (result != FocusResult::OK) {
-        return result;
-    }
-
-    std::string_view reason =
-            (request.focusedToken) ? "setFocusedWindow_FocusCheck" : "setFocusedWindow";
-    onFocusChangedLocked(oldFocusedToken, newFocusedToken, displayId, reason);
-    return FocusResult::OK;
-}
-
-void InputDispatcher::onFocusChangedLocked(const sp<IBinder>& oldFocusedToken,
-                                           const sp<IBinder>& newFocusedToken, int32_t displayId,
-                                           std::string_view reason) {
-    if (oldFocusedToken) {
-        std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(oldFocusedToken);
+void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes) {
+    if (changes.oldFocus) {
+        std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(changes.oldFocus);
         if (focusedInputChannel) {
             CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
                                        "focus left window");
             synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
-            enqueueFocusEventLocked(oldFocusedToken, false /*hasFocus*/, reason);
+            enqueueFocusEventLocked(changes.oldFocus, false /*hasFocus*/, changes.reason);
         }
-        mFocusedWindowTokenByDisplay.erase(displayId);
     }
-    if (newFocusedToken) {
-        mFocusedWindowTokenByDisplay[displayId] = newFocusedToken;
-        enqueueFocusEventLocked(newFocusedToken, true /*hasFocus*/, reason);
+    if (changes.newFocus) {
+        enqueueFocusEventLocked(changes.newFocus, true /*hasFocus*/, changes.reason);
     }
 
     // If a window has pointer capture, then it must have focus. We need to ensure that this
@@ -5972,8 +5840,8 @@
     // front.
     disablePointerCaptureForcedLocked();
 
-    if (mFocusedDisplayId == displayId) {
-        notifyFocusChangedLocked(oldFocusedToken, newFocusedToken);
+    if (mFocusedDisplayId == changes.displayId) {
+        notifyFocusChangedLocked(changes.oldFocus, changes.newFocus);
     }
 }
 
@@ -6006,50 +5874,6 @@
     mInboundQueue.push_front(std::move(entry));
 }
 
-/**
- * Checks if the window token can be focused on a display. The token can be focused if there is
- * at least one window handle that is visible with the same token and all window handles with the
- * same token are focusable.
- *
- * In the case of mirroring, two windows may share the same window token and their visibility
- * might be different. Example, the mirrored window can cover the window its mirroring. However,
- * 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.
- */
-InputDispatcher::FocusResult InputDispatcher::checkTokenFocusableLocked(const sp<IBinder>& token,
-                                                                        int32_t displayId) const {
-    bool allWindowsAreFocusable = true;
-    bool visibleWindowFound = false;
-    bool windowFound = false;
-    for (const sp<InputWindowHandle>& window : getWindowHandlesLocked(displayId)) {
-        if (window->getToken() != token) {
-            continue;
-        }
-        windowFound = true;
-        if (window->getInfo()->visible) {
-            // Check if at least a single window is visible.
-            visibleWindowFound = true;
-        }
-        if (!window->getInfo()->focusable) {
-            // Check if all windows with the window token are focusable.
-            allWindowsAreFocusable = false;
-            break;
-        }
-    }
-
-    if (!windowFound) {
-        return FocusResult::NO_WINDOW;
-    }
-    if (!allWindowsAreFocusable) {
-        return FocusResult::NOT_FOCUSABLE;
-    }
-    if (!visibleWindowFound) {
-        return FocusResult::NOT_VISIBLE;
-    }
-
-    return FocusResult::OK;
-}
-
 void InputDispatcher::setPointerCaptureLocked(bool enabled) {
     std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
             &InputDispatcher::doSetPointerCaptureLockedInterruptible);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index c7299e9..02f5b87 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -20,6 +20,7 @@
 #include "AnrTracker.h"
 #include "CancelationOptions.h"
 #include "Entry.h"
+#include "FocusResolver.h"
 #include "InjectionState.h"
 #include "InputDispatcherConfiguration.h"
 #include "InputDispatcherInterface.h"
@@ -146,14 +147,6 @@
         NO_POINTER_CAPTURE,
     };
 
-    enum class FocusResult {
-        OK,
-        NO_WINDOW,
-        NOT_FOCUSABLE,
-        NOT_VISIBLE,
-    };
-    static const char* typeToString(FocusResult result);
-
     std::unique_ptr<InputThread> mThread;
 
     sp<InputDispatcherPolicyInterface> mPolicy;
@@ -190,7 +183,7 @@
 
     // Enqueues a focus event.
     void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
-                                 std::string_view reason) REQUIRES(mLock);
+                                 const std::string& reason) REQUIRES(mLock);
 
     // Adds an event to a queue of recent events for debugging purposes.
     void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
@@ -333,9 +326,6 @@
     sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock);
     bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
     bool hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const REQUIRES(mLock);
-    FocusResult handleFocusRequestLocked(const FocusRequest&) REQUIRES(mLock);
-    FocusResult checkTokenFocusableLocked(const sp<IBinder>& token, int32_t displayId) const
-            REQUIRES(mLock);
 
     /*
      * Validate and update InputWindowHandles for a given display.
@@ -344,12 +334,6 @@
             const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId)
             REQUIRES(mLock);
 
-    // 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 the same
-    // token. Focus is tracked by the token per display and the events are dispatched to the
-    // channel associated by this token.
-    std::unordered_map<int32_t, sp<IBinder>> mFocusedWindowTokenByDisplay GUARDED_BY(mLock);
-
     std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
 
     // Focused applications.
@@ -359,6 +343,8 @@
     // Top focused display.
     int32_t mFocusedDisplayId GUARDED_BY(mLock);
 
+    // Keeps track of the focused window per display and determines focus changes.
+    FocusResolver mFocusResolver GUARDED_BY(mLock);
     // Whether the focused window on the focused display has requested Pointer Capture.
     // The state of this variable should always be in sync with the state of Pointer Capture in the
     // policy, which is updated through setPointerCaptureLocked(enabled).
@@ -462,13 +448,6 @@
      */
     void sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) REQUIRES(mLock);
 
-    /**
-     * This map will store the pending focus requests 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
-            GUARDED_BY(mLock);
 
     // Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
     // AnrTracker must be kept in-sync with all responsive connection.waitQueues.
@@ -586,8 +565,6 @@
     void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
     void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
     void logDispatchStateLocked() REQUIRES(mLock);
-    std::string dumpFocusedWindowsLocked() REQUIRES(mLock);
-    std::string dumpPendingFocusRequestsLocked() REQUIRES(mLock);
     std::string dumpPointerCaptureStateLocked() REQUIRES(mLock);
 
     // Registration.
@@ -603,8 +580,7 @@
                                        uint32_t seq, bool handled) REQUIRES(mLock);
     void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
             REQUIRES(mLock);
-    void onFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus,
-                              int32_t displayId, std::string_view reason) REQUIRES(mLock);
+    void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
     void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
             REQUIRES(mLock);
     void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 8cb7194..959dadc 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -30,6 +30,7 @@
         "AnrTracker_test.cpp",
         "BlockingQueue_test.cpp",
         "EventHub_test.cpp",
+        "FocusResolver_test.cpp",
         "IInputFlingerQuery.aidl",
         "InputClassifier_test.cpp",
         "InputClassifierConverter_test.cpp",
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
new file mode 100644
index 0000000..ef3dd65
--- /dev/null
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "../FocusResolver.h"
+
+// atest inputflinger_tests:FocusResolverTest
+
+namespace android::inputdispatcher {
+
+class FakeWindowHandle : public InputWindowHandle {
+public:
+    FakeWindowHandle(const std::string& name, const sp<IBinder>& token, bool focusable,
+                     bool visible) {
+        mInfo.token = token;
+        mInfo.name = name;
+        mInfo.visible = visible;
+        mInfo.focusable = focusable;
+    }
+
+    bool updateInfo() { return true; }
+    void setFocusable(bool focusable) { mInfo.focusable = focusable; }
+    void setVisible(bool visible) { mInfo.visible = visible; }
+};
+
+TEST(FocusResolverTest, SetFocusedWindow) {
+    sp<IBinder> focusableWindowToken = new BBinder();
+    sp<IBinder> invisibleWindowToken = new BBinder();
+    sp<IBinder> unfocusableWindowToken = new BBinder();
+    std::vector<sp<InputWindowHandle>> windows;
+    windows.push_back(new FakeWindowHandle("Focusable", focusableWindowToken, true /* focusable */,
+                                           true /* visible */));
+    windows.push_back(new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */,
+                                           false /* visible */));
+    windows.push_back(new FakeWindowHandle("unfocusable", unfocusableWindowToken,
+                                           false /* focusable */, true /* visible */));
+
+    // focusable window can get focused
+    FocusRequest request;
+    request.displayId = 42;
+    request.token = focusableWindowToken;
+    FocusResolver focusResolver;
+    std::optional<FocusResolver::FocusChanges> changes =
+            focusResolver.setFocusedWindow(request, windows);
+    ASSERT_EQ(nullptr, changes->oldFocus);
+    ASSERT_EQ(focusableWindowToken, changes->newFocus);
+    ASSERT_EQ(request.displayId, changes->displayId);
+
+    // invisible window cannot get focused
+    request.token = invisibleWindowToken;
+    changes = focusResolver.setFocusedWindow(request, windows);
+    ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+    ASSERT_EQ(nullptr, changes->newFocus);
+
+    // unfocusableWindowToken window cannot get focused
+    request.token = unfocusableWindowToken;
+    changes = focusResolver.setFocusedWindow(request, windows);
+    ASSERT_FALSE(changes);
+}
+
+TEST(FocusResolverTest, SetFocusedMirroredWindow) {
+    sp<IBinder> focusableWindowToken = new BBinder();
+    sp<IBinder> invisibleWindowToken = new BBinder();
+    sp<IBinder> unfocusableWindowToken = new BBinder();
+    std::vector<sp<InputWindowHandle>> windows;
+    windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */,
+                                           true /* visible */));
+    windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */,
+                                           true /* visible */));
+
+    windows.push_back(new FakeWindowHandle("Mirror2Visible", invisibleWindowToken,
+                                           true /* focusable */, true /* visible */));
+    windows.push_back(new FakeWindowHandle("Mirror2Invisible", invisibleWindowToken,
+                                           true /* focusable */, false /* visible */));
+
+    windows.push_back(new FakeWindowHandle("Mirror3Focusable", unfocusableWindowToken,
+                                           true /* focusable */, true /* visible */));
+    windows.push_back(new FakeWindowHandle("Mirror3Unfocusable", unfocusableWindowToken,
+                                           false /* focusable */, true /* visible */));
+
+    // mirrored window can get focused
+    FocusRequest request;
+    request.displayId = 42;
+    request.token = focusableWindowToken;
+    FocusResolver focusResolver;
+    std::optional<FocusResolver::FocusChanges> changes =
+            focusResolver.setFocusedWindow(request, windows);
+    ASSERT_EQ(nullptr, changes->oldFocus);
+    ASSERT_EQ(focusableWindowToken, changes->newFocus);
+
+    // 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);
+
+    // mirrored window with one or more unfocusable window cannot get focused
+    request.token = unfocusableWindowToken;
+    changes = focusResolver.setFocusedWindow(request, windows);
+    ASSERT_FALSE(changes);
+}
+
+TEST(FocusResolverTest, SetInputWindows) {
+    sp<IBinder> focusableWindowToken = new BBinder();
+    std::vector<sp<InputWindowHandle>> windows;
+    sp<FakeWindowHandle> window = new FakeWindowHandle("Focusable", focusableWindowToken,
+                                                       true /* focusable */, true /* visible */);
+    windows.push_back(window);
+
+    // focusable window can get focused
+    FocusRequest request;
+    request.displayId = 42;
+    request.token = focusableWindowToken;
+    FocusResolver focusResolver;
+    std::optional<FocusResolver::FocusChanges> changes =
+            focusResolver.setFocusedWindow(request, windows);
+    ASSERT_EQ(focusableWindowToken, changes->newFocus);
+
+    // Window visibility changes and the window loses focused
+    window->setVisible(false);
+    changes = focusResolver.setInputWindows(request.displayId, windows);
+    ASSERT_EQ(nullptr, changes->newFocus);
+    ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+}
+
+TEST(FocusResolverTest, FocusRequestsCanBePending) {
+    sp<IBinder> invisibleWindowToken = new BBinder();
+    std::vector<sp<InputWindowHandle>> windows;
+
+    sp<FakeWindowHandle> invisibleWindow =
+            new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */,
+                                 false /* visible */);
+    windows.push_back(invisibleWindow);
+
+    // invisible window cannot get focused
+    FocusRequest request;
+    request.displayId = 42;
+    request.token = invisibleWindowToken;
+    FocusResolver focusResolver;
+    std::optional<FocusResolver::FocusChanges> changes =
+            focusResolver.setFocusedWindow(request, windows);
+    ASSERT_FALSE(changes);
+
+    // 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);
+}
+
+} // namespace android::inputdispatcher