| 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 |