Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2021 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 16 | #include <optional> |
Arthur Hung | b060def | 2022-05-26 07:41:33 +0000 | [diff] [blame] | 17 | #define LOG_TAG "InputDispatcher" |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 18 | #define ATRACE_TAG ATRACE_TAG_INPUT |
| 19 | |
| 20 | #define INDENT " " |
| 21 | #define INDENT2 " " |
| 22 | |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 23 | #include <inttypes.h> |
| 24 | |
| 25 | #include <android-base/stringprintf.h> |
| 26 | #include <binder/Binder.h> |
Dominik Laskowski | 7578845 | 2021-02-09 18:51:25 -0800 | [diff] [blame] | 27 | #include <ftl/enum.h> |
chaviw | 98318de | 2021-05-19 16:45:23 -0500 | [diff] [blame] | 28 | #include <gui/WindowInfo.h> |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 29 | #include <unordered_set> |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 30 | |
Arthur Hung | b060def | 2022-05-26 07:41:33 +0000 | [diff] [blame] | 31 | #include "DebugConfig.h" |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 32 | #include "FocusResolver.h" |
| 33 | |
chaviw | 98318de | 2021-05-19 16:45:23 -0500 | [diff] [blame] | 34 | using android::gui::FocusRequest; |
| 35 | using android::gui::WindowInfoHandle; |
| 36 | |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 37 | namespace android::inputdispatcher { |
| 38 | |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 39 | template <typename T> |
| 40 | struct SpHash { |
| 41 | size_t operator()(const sp<T>& k) const { return std::hash<T*>()(k.get()); } |
| 42 | }; |
| 43 | |
Linnan Li | 13bf76a | 2024-05-05 19:18:02 +0800 | [diff] [blame^] | 44 | sp<IBinder> FocusResolver::getFocusedWindowToken(ui::LogicalDisplayId displayId) const { |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 45 | auto it = mFocusedWindowTokenByDisplay.find(displayId); |
| 46 | return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr; |
| 47 | } |
| 48 | |
Linnan Li | 13bf76a | 2024-05-05 19:18:02 +0800 | [diff] [blame^] | 49 | std::optional<FocusRequest> FocusResolver::getFocusRequest(ui::LogicalDisplayId displayId) { |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 50 | auto it = mFocusRequestByDisplay.find(displayId); |
| 51 | return it != mFocusRequestByDisplay.end() ? std::make_optional<>(it->second) : std::nullopt; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 52 | } |
| 53 | |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 54 | /** |
| 55 | * 'setInputWindows' is called when the window properties change. Here we will check whether the |
| 56 | * currently focused window can remain focused. If the currently focused window remains eligible |
| 57 | * for focus ('isTokenFocusable' returns OK), then we will continue to grant it focus otherwise |
| 58 | * we will check if the previous focus request is eligible to receive focus. |
| 59 | */ |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 60 | std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows( |
Linnan Li | 13bf76a | 2024-05-05 19:18:02 +0800 | [diff] [blame^] | 61 | ui::LogicalDisplayId displayId, const std::vector<sp<WindowInfoHandle>>& windows) { |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 62 | std::string removeFocusReason; |
| 63 | |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 64 | const std::optional<FocusRequest> request = getFocusRequest(displayId); |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 65 | const sp<IBinder> currentFocus = getFocusedWindowToken(displayId); |
| 66 | |
| 67 | // Find the next focused token based on the latest FocusRequest. If the requested focus window |
| 68 | // cannot be focused, focus will be removed. |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 69 | if (request) { |
| 70 | sp<IBinder> requestedFocus = request->token; |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 71 | sp<WindowInfoHandle> resolvedFocusWindow; |
| 72 | Focusability result = getResolvedFocusWindow(requestedFocus, windows, resolvedFocusWindow); |
| 73 | if (result == Focusability::OK && resolvedFocusWindow->getToken() == currentFocus) { |
| 74 | return std::nullopt; |
| 75 | } |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 76 | const Focusability previousResult = mLastFocusResultByDisplay[displayId]; |
| 77 | mLastFocusResultByDisplay[displayId] = result; |
| 78 | if (result == Focusability::OK) { |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 79 | LOG_ALWAYS_FATAL_IF(!resolvedFocusWindow, |
| 80 | "Focused window should be non-null when result is OK!"); |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 81 | return updateFocusedWindow(displayId, |
| 82 | "Window became focusable. Previous reason: " + |
Dominik Laskowski | 7578845 | 2021-02-09 18:51:25 -0800 | [diff] [blame] | 83 | ftl::enum_string(previousResult), |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 84 | resolvedFocusWindow->getToken(), |
| 85 | resolvedFocusWindow->getName()); |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 86 | } |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 87 | removeFocusReason = ftl::enum_string(result); |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 88 | } |
| 89 | |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 90 | // Focused window is no longer focusable and we don't have a suitable focus request to grant. |
| 91 | // Remove focus if needed. |
| 92 | return updateFocusedWindow(displayId, removeFocusReason, nullptr); |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 93 | } |
| 94 | |
| 95 | std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow( |
chaviw | 98318de | 2021-05-19 16:45:23 -0500 | [diff] [blame] | 96 | const FocusRequest& request, const std::vector<sp<WindowInfoHandle>>& windows) { |
Linnan Li | 13bf76a | 2024-05-05 19:18:02 +0800 | [diff] [blame^] | 97 | const ui::LogicalDisplayId displayId = ui::LogicalDisplayId{request.displayId}; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 98 | const sp<IBinder> currentFocus = getFocusedWindowToken(displayId); |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 99 | if (currentFocus == request.token) { |
Linnan Li | 13bf76a | 2024-05-05 19:18:02 +0800 | [diff] [blame^] | 100 | ALOGD_IF(DEBUG_FOCUS, "setFocusedWindow %s on display %s ignored, reason: already focused", |
| 101 | request.windowName.c_str(), displayId.toString().c_str()); |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 102 | return std::nullopt; |
| 103 | } |
| 104 | |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 105 | sp<WindowInfoHandle> resolvedFocusWindow; |
| 106 | Focusability result = getResolvedFocusWindow(request.token, windows, resolvedFocusWindow); |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 107 | // Update focus request. The focus resolver will always try to handle this request if there is |
| 108 | // no focused window on the display. |
| 109 | mFocusRequestByDisplay[displayId] = request; |
| 110 | mLastFocusResultByDisplay[displayId] = result; |
| 111 | |
| 112 | if (result == Focusability::OK) { |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 113 | LOG_ALWAYS_FATAL_IF(!resolvedFocusWindow, |
| 114 | "Focused window should be non-null when result is OK!"); |
| 115 | return updateFocusedWindow(displayId, "setFocusedWindow", resolvedFocusWindow->getToken(), |
| 116 | resolvedFocusWindow->getName()); |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 117 | } |
| 118 | |
| 119 | // The requested window is not currently focusable. Wait for the window to become focusable |
| 120 | // but remove focus from the current window so that input events can go into a pending queue |
| 121 | // and be sent to the window when it becomes focused. |
Dominik Laskowski | 7578845 | 2021-02-09 18:51:25 -0800 | [diff] [blame] | 122 | return updateFocusedWindow(displayId, "Waiting for window because " + ftl::enum_string(result), |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 123 | nullptr); |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 124 | } |
| 125 | |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 126 | FocusResolver::Focusability FocusResolver::getResolvedFocusWindow( |
| 127 | const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows, |
| 128 | sp<WindowInfoHandle>& outFocusableWindow) { |
| 129 | sp<IBinder> curFocusCandidate = token; |
| 130 | bool focusedWindowFound = false; |
| 131 | |
| 132 | // Keep track of all windows reached to prevent a cyclical transferFocus request. |
| 133 | std::unordered_set<sp<IBinder>, SpHash<IBinder>> tokensReached; |
| 134 | |
| 135 | while (curFocusCandidate != nullptr && tokensReached.count(curFocusCandidate) == 0) { |
| 136 | tokensReached.emplace(curFocusCandidate); |
| 137 | Focusability result = isTokenFocusable(curFocusCandidate, windows, outFocusableWindow); |
| 138 | if (result == Focusability::OK) { |
| 139 | LOG_ALWAYS_FATAL_IF(!outFocusableWindow, |
| 140 | "Focused window should be non-null when result is OK!"); |
| 141 | focusedWindowFound = true; |
| 142 | // outFocusableWindow has been updated by isTokenFocusable to contain |
| 143 | // the window info for curFocusCandidate. See if we can grant focus |
| 144 | // to the token that it wants to transfer its focus to. |
| 145 | curFocusCandidate = outFocusableWindow->getInfo()->focusTransferTarget; |
| 146 | } |
| 147 | |
| 148 | // If the initial token is not focusable, return early with the failed result. |
| 149 | if (!focusedWindowFound) { |
| 150 | return result; |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | return focusedWindowFound ? Focusability::OK : Focusability::NO_WINDOW; |
| 155 | } |
| 156 | |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 157 | FocusResolver::Focusability FocusResolver::isTokenFocusable( |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 158 | const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows, |
| 159 | sp<WindowInfoHandle>& outFocusableWindow) { |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 160 | bool allWindowsAreFocusable = true; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 161 | bool windowFound = false; |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 162 | sp<WindowInfoHandle> visibleWindowHandle = nullptr; |
chaviw | 98318de | 2021-05-19 16:45:23 -0500 | [diff] [blame] | 163 | for (const sp<WindowInfoHandle>& window : windows) { |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 164 | if (window->getToken() != token) { |
| 165 | continue; |
| 166 | } |
| 167 | windowFound = true; |
Prabir Pradhan | 4d5c52f | 2022-01-31 08:52:10 -0800 | [diff] [blame] | 168 | if (!window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)) { |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 169 | // Check if at least a single window is visible. |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 170 | visibleWindowHandle = window; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 171 | } |
Prabir Pradhan | 4d5c52f | 2022-01-31 08:52:10 -0800 | [diff] [blame] | 172 | if (window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE)) { |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 173 | // Check if all windows with the window token are focusable. |
| 174 | allWindowsAreFocusable = false; |
| 175 | break; |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | if (!windowFound) { |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 180 | return Focusability::NO_WINDOW; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 181 | } |
| 182 | if (!allWindowsAreFocusable) { |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 183 | return Focusability::NOT_FOCUSABLE; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 184 | } |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 185 | if (!visibleWindowHandle) { |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 186 | return Focusability::NOT_VISIBLE; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 187 | } |
| 188 | |
Chavi Weingarten | 847e851 | 2023-03-29 00:26:09 +0000 | [diff] [blame] | 189 | // Only set the outFoundWindow if the window can be focused |
| 190 | outFocusableWindow = visibleWindowHandle; |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 191 | return Focusability::OK; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 192 | } |
| 193 | |
| 194 | std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow( |
Linnan Li | 13bf76a | 2024-05-05 19:18:02 +0800 | [diff] [blame^] | 195 | ui::LogicalDisplayId displayId, const std::string& reason, const sp<IBinder>& newFocus, |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 196 | const std::string& tokenName) { |
| 197 | sp<IBinder> oldFocus = getFocusedWindowToken(displayId); |
| 198 | if (newFocus == oldFocus) { |
| 199 | return std::nullopt; |
| 200 | } |
| 201 | if (newFocus) { |
| 202 | mFocusedWindowTokenByDisplay[displayId] = {tokenName, newFocus}; |
| 203 | } else { |
| 204 | mFocusedWindowTokenByDisplay.erase(displayId); |
| 205 | } |
| 206 | |
| 207 | return {{oldFocus, newFocus, displayId, reason}}; |
| 208 | } |
| 209 | |
| 210 | std::string FocusResolver::dumpFocusedWindows() const { |
| 211 | if (mFocusedWindowTokenByDisplay.empty()) { |
| 212 | return INDENT "FocusedWindows: <none>\n"; |
| 213 | } |
| 214 | |
| 215 | std::string dump; |
| 216 | dump += INDENT "FocusedWindows:\n"; |
| 217 | for (const auto& [displayId, namedToken] : mFocusedWindowTokenByDisplay) { |
Linnan Li | 13bf76a | 2024-05-05 19:18:02 +0800 | [diff] [blame^] | 218 | dump += base::StringPrintf(INDENT2 "displayId=%s, name='%s'\n", |
| 219 | displayId.toString().c_str(), namedToken.first.c_str()); |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 220 | } |
| 221 | return dump; |
| 222 | } |
| 223 | |
| 224 | std::string FocusResolver::dump() const { |
| 225 | std::string dump = dumpFocusedWindows(); |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 226 | if (mFocusRequestByDisplay.empty()) { |
| 227 | return dump + INDENT "FocusRequests: <none>\n"; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 228 | } |
| 229 | |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 230 | dump += INDENT "FocusRequests:\n"; |
| 231 | for (const auto& [displayId, request] : mFocusRequestByDisplay) { |
| 232 | auto it = mLastFocusResultByDisplay.find(displayId); |
| 233 | std::string result = |
Dominik Laskowski | 7578845 | 2021-02-09 18:51:25 -0800 | [diff] [blame] | 234 | it != mLastFocusResultByDisplay.end() ? ftl::enum_string(it->second) : ""; |
Linnan Li | 13bf76a | 2024-05-05 19:18:02 +0800 | [diff] [blame^] | 235 | dump += base::StringPrintf(INDENT2 "displayId=%s, name='%s' result='%s'\n", |
| 236 | displayId.toString().c_str(), request.windowName.c_str(), |
| 237 | result.c_str()); |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 238 | } |
| 239 | return dump; |
| 240 | } |
| 241 | |
Linnan Li | 13bf76a | 2024-05-05 19:18:02 +0800 | [diff] [blame^] | 242 | void FocusResolver::displayRemoved(ui::LogicalDisplayId displayId) { |
Vishnu Nair | 599f141 | 2021-06-21 10:39:58 -0700 | [diff] [blame] | 243 | mFocusRequestByDisplay.erase(displayId); |
| 244 | mLastFocusResultByDisplay.erase(displayId); |
| 245 | } |
| 246 | |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 247 | } // namespace android::inputdispatcher |