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/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);