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