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 | */ |
| 16 | |
| 17 | #define LOG_TAG "FocusResolver" |
| 18 | #define ATRACE_TAG ATRACE_TAG_INPUT |
| 19 | |
| 20 | #define INDENT " " |
| 21 | #define INDENT2 " " |
| 22 | |
| 23 | // Log debug messages about input focus tracking. |
| 24 | static constexpr bool DEBUG_FOCUS = false; |
| 25 | |
| 26 | #include <inttypes.h> |
| 27 | |
| 28 | #include <android-base/stringprintf.h> |
| 29 | #include <binder/Binder.h> |
| 30 | #include <input/InputWindow.h> |
| 31 | #include <input/NamedEnum.h> |
| 32 | #include <log/log.h> |
| 33 | |
| 34 | #include "FocusResolver.h" |
| 35 | |
| 36 | namespace android::inputdispatcher { |
| 37 | |
| 38 | sp<IBinder> FocusResolver::getFocusedWindowToken(int32_t displayId) const { |
| 39 | auto it = mFocusedWindowTokenByDisplay.find(displayId); |
| 40 | return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr; |
| 41 | } |
| 42 | |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 43 | std::optional<FocusRequest> FocusResolver::getFocusRequest(int32_t displayId) { |
| 44 | auto it = mFocusRequestByDisplay.find(displayId); |
| 45 | return it != mFocusRequestByDisplay.end() ? std::make_optional<>(it->second) : std::nullopt; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 46 | } |
| 47 | |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 48 | /** |
| 49 | * 'setInputWindows' is called when the window properties change. Here we will check whether the |
| 50 | * currently focused window can remain focused. If the currently focused window remains eligible |
| 51 | * for focus ('isTokenFocusable' returns OK), then we will continue to grant it focus otherwise |
| 52 | * we will check if the previous focus request is eligible to receive focus. |
| 53 | */ |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 54 | std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows( |
| 55 | int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows) { |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 56 | std::string removeFocusReason; |
| 57 | |
| 58 | // Check if the currently focused window is still focusable. |
| 59 | const sp<IBinder> currentFocus = getFocusedWindowToken(displayId); |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 60 | if (currentFocus) { |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 61 | Focusability result = isTokenFocusable(currentFocus, windows); |
| 62 | if (result == Focusability::OK) { |
| 63 | return std::nullopt; |
| 64 | } |
| 65 | removeFocusReason = NamedEnum::string(result); |
| 66 | } |
| 67 | |
| 68 | // We don't have a focused window or the currently focused window is no longer focusable. Check |
| 69 | // to see if we can grant focus to the window that previously requested focus. |
| 70 | const std::optional<FocusRequest> request = getFocusRequest(displayId); |
| 71 | if (request) { |
| 72 | sp<IBinder> requestedFocus = request->token; |
| 73 | const Focusability result = isTokenFocusable(requestedFocus, windows); |
| 74 | const Focusability previousResult = mLastFocusResultByDisplay[displayId]; |
| 75 | mLastFocusResultByDisplay[displayId] = result; |
| 76 | if (result == Focusability::OK) { |
| 77 | return updateFocusedWindow(displayId, |
| 78 | "Window became focusable. Previous reason: " + |
| 79 | NamedEnum::string(previousResult), |
| 80 | requestedFocus, request->windowName); |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 81 | } |
| 82 | } |
| 83 | |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 84 | // Focused window is no longer focusable and we don't have a suitable focus request to grant. |
| 85 | // Remove focus if needed. |
| 86 | return updateFocusedWindow(displayId, removeFocusReason, nullptr); |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 87 | } |
| 88 | |
| 89 | std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow( |
| 90 | const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows) { |
| 91 | const int32_t displayId = request.displayId; |
| 92 | const sp<IBinder> currentFocus = getFocusedWindowToken(displayId); |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 93 | if (currentFocus == request.token) { |
| 94 | ALOGD_IF(DEBUG_FOCUS, |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 95 | "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused", |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 96 | request.windowName.c_str(), displayId); |
| 97 | return std::nullopt; |
| 98 | } |
| 99 | |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 100 | // Handle conditional focus requests, i.e. requests that have a focused token. These requests |
| 101 | // are not persistent. If the window is no longer focusable, we expect focus to go back to the |
| 102 | // previously focused window. |
| 103 | if (request.focusedToken) { |
| 104 | if (currentFocus != request.focusedToken) { |
| 105 | ALOGW("setFocusedWindow %s on display %" PRId32 |
| 106 | " ignored, reason: focusedToken %s is not focused", |
| 107 | request.windowName.c_str(), displayId, request.focusedWindowName.c_str()); |
| 108 | return std::nullopt; |
| 109 | } |
| 110 | Focusability result = isTokenFocusable(request.token, windows); |
| 111 | if (result == Focusability::OK) { |
| 112 | return updateFocusedWindow(displayId, "setFocusedWindow with focus check", |
| 113 | request.token, request.windowName); |
| 114 | } |
| 115 | ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason: %s", |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 116 | request.windowName.c_str(), displayId, NamedEnum::string(result).c_str()); |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 117 | return std::nullopt; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 118 | } |
| 119 | |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 120 | Focusability result = isTokenFocusable(request.token, windows); |
| 121 | // Update focus request. The focus resolver will always try to handle this request if there is |
| 122 | // no focused window on the display. |
| 123 | mFocusRequestByDisplay[displayId] = request; |
| 124 | mLastFocusResultByDisplay[displayId] = result; |
| 125 | |
| 126 | if (result == Focusability::OK) { |
| 127 | return updateFocusedWindow(displayId, "setFocusedWindow", request.token, |
| 128 | request.windowName); |
| 129 | } |
| 130 | |
| 131 | // The requested window is not currently focusable. Wait for the window to become focusable |
| 132 | // but remove focus from the current window so that input events can go into a pending queue |
| 133 | // and be sent to the window when it becomes focused. |
| 134 | return updateFocusedWindow(displayId, "Waiting for window because " + NamedEnum::string(result), |
| 135 | nullptr); |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 136 | } |
| 137 | |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 138 | FocusResolver::Focusability FocusResolver::isTokenFocusable( |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 139 | const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) { |
| 140 | bool allWindowsAreFocusable = true; |
| 141 | bool visibleWindowFound = false; |
| 142 | bool windowFound = false; |
| 143 | for (const sp<InputWindowHandle>& window : windows) { |
| 144 | if (window->getToken() != token) { |
| 145 | continue; |
| 146 | } |
| 147 | windowFound = true; |
| 148 | if (window->getInfo()->visible) { |
| 149 | // Check if at least a single window is visible. |
| 150 | visibleWindowFound = true; |
| 151 | } |
| 152 | if (!window->getInfo()->focusable) { |
| 153 | // Check if all windows with the window token are focusable. |
| 154 | allWindowsAreFocusable = false; |
| 155 | break; |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | if (!windowFound) { |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 160 | return Focusability::NO_WINDOW; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 161 | } |
| 162 | if (!allWindowsAreFocusable) { |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 163 | return Focusability::NOT_FOCUSABLE; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 164 | } |
| 165 | if (!visibleWindowFound) { |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 166 | return Focusability::NOT_VISIBLE; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 167 | } |
| 168 | |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 169 | return Focusability::OK; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 170 | } |
| 171 | |
| 172 | std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow( |
| 173 | int32_t displayId, const std::string& reason, const sp<IBinder>& newFocus, |
| 174 | const std::string& tokenName) { |
| 175 | sp<IBinder> oldFocus = getFocusedWindowToken(displayId); |
| 176 | if (newFocus == oldFocus) { |
| 177 | return std::nullopt; |
| 178 | } |
| 179 | if (newFocus) { |
| 180 | mFocusedWindowTokenByDisplay[displayId] = {tokenName, newFocus}; |
| 181 | } else { |
| 182 | mFocusedWindowTokenByDisplay.erase(displayId); |
| 183 | } |
| 184 | |
| 185 | return {{oldFocus, newFocus, displayId, reason}}; |
| 186 | } |
| 187 | |
| 188 | std::string FocusResolver::dumpFocusedWindows() const { |
| 189 | if (mFocusedWindowTokenByDisplay.empty()) { |
| 190 | return INDENT "FocusedWindows: <none>\n"; |
| 191 | } |
| 192 | |
| 193 | std::string dump; |
| 194 | dump += INDENT "FocusedWindows:\n"; |
| 195 | for (const auto& [displayId, namedToken] : mFocusedWindowTokenByDisplay) { |
| 196 | dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId, |
| 197 | namedToken.first.c_str()); |
| 198 | } |
| 199 | return dump; |
| 200 | } |
| 201 | |
| 202 | std::string FocusResolver::dump() const { |
| 203 | std::string dump = dumpFocusedWindows(); |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 204 | if (mFocusRequestByDisplay.empty()) { |
| 205 | return dump + INDENT "FocusRequests: <none>\n"; |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 206 | } |
| 207 | |
Vishnu Nair | 1dcad98 | 2021-02-24 14:38:25 -0800 | [diff] [blame] | 208 | dump += INDENT "FocusRequests:\n"; |
| 209 | for (const auto& [displayId, request] : mFocusRequestByDisplay) { |
| 210 | auto it = mLastFocusResultByDisplay.find(displayId); |
| 211 | std::string result = |
| 212 | it != mLastFocusResultByDisplay.end() ? NamedEnum::string(it->second) : ""; |
| 213 | dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s' result='%s'\n", |
| 214 | displayId, request.windowName.c_str(), result.c_str()); |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 215 | } |
| 216 | return dump; |
| 217 | } |
| 218 | |
Vishnu Nair | 599f141 | 2021-06-21 10:39:58 -0700 | [diff] [blame] | 219 | void FocusResolver::displayRemoved(int32_t displayId) { |
| 220 | mFocusRequestByDisplay.erase(displayId); |
| 221 | mLastFocusResultByDisplay.erase(displayId); |
| 222 | } |
| 223 | |
Vishnu Nair | c519ff7 | 2021-01-21 08:23:08 -0800 | [diff] [blame] | 224 | } // namespace android::inputdispatcher |