blob: fb194355ba330e5276e7868f5584368a9b3748ac [file] [log] [blame]
Vishnu Nairc519ff72021-01-21 08:23:08 -08001/*
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.
24static 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
36namespace android::inputdispatcher {
37
38sp<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 Nair1dcad982021-02-24 14:38:25 -080043std::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 Nairc519ff72021-01-21 08:23:08 -080046}
47
Vishnu Nair1dcad982021-02-24 14:38:25 -080048/**
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 Nairc519ff72021-01-21 08:23:08 -080054std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
55 int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows) {
Vishnu Nair1dcad982021-02-24 14:38:25 -080056 std::string removeFocusReason;
57
58 // Check if the currently focused window is still focusable.
59 const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
Vishnu Nairc519ff72021-01-21 08:23:08 -080060 if (currentFocus) {
Vishnu Nair1dcad982021-02-24 14:38:25 -080061 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 Nairc519ff72021-01-21 08:23:08 -080081 }
82 }
83
Vishnu Nair1dcad982021-02-24 14:38:25 -080084 // 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 Nairc519ff72021-01-21 08:23:08 -080087}
88
89std::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 Nairc519ff72021-01-21 08:23:08 -080093 if (currentFocus == request.token) {
94 ALOGD_IF(DEBUG_FOCUS,
Vishnu Nair1dcad982021-02-24 14:38:25 -080095 "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
Vishnu Nairc519ff72021-01-21 08:23:08 -080096 request.windowName.c_str(), displayId);
97 return std::nullopt;
98 }
99
Vishnu Nair1dcad982021-02-24 14:38:25 -0800100 // 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 Nairc519ff72021-01-21 08:23:08 -0800116 request.windowName.c_str(), displayId, NamedEnum::string(result).c_str());
Vishnu Nair1dcad982021-02-24 14:38:25 -0800117 return std::nullopt;
Vishnu Nairc519ff72021-01-21 08:23:08 -0800118 }
119
Vishnu Nair1dcad982021-02-24 14:38:25 -0800120 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 Nairc519ff72021-01-21 08:23:08 -0800136}
137
Vishnu Nair1dcad982021-02-24 14:38:25 -0800138FocusResolver::Focusability FocusResolver::isTokenFocusable(
Vishnu Nairc519ff72021-01-21 08:23:08 -0800139 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 Nair1dcad982021-02-24 14:38:25 -0800160 return Focusability::NO_WINDOW;
Vishnu Nairc519ff72021-01-21 08:23:08 -0800161 }
162 if (!allWindowsAreFocusable) {
Vishnu Nair1dcad982021-02-24 14:38:25 -0800163 return Focusability::NOT_FOCUSABLE;
Vishnu Nairc519ff72021-01-21 08:23:08 -0800164 }
165 if (!visibleWindowFound) {
Vishnu Nair1dcad982021-02-24 14:38:25 -0800166 return Focusability::NOT_VISIBLE;
Vishnu Nairc519ff72021-01-21 08:23:08 -0800167 }
168
Vishnu Nair1dcad982021-02-24 14:38:25 -0800169 return Focusability::OK;
Vishnu Nairc519ff72021-01-21 08:23:08 -0800170}
171
172std::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
188std::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
202std::string FocusResolver::dump() const {
203 std::string dump = dumpFocusedWindows();
Vishnu Nair1dcad982021-02-24 14:38:25 -0800204 if (mFocusRequestByDisplay.empty()) {
205 return dump + INDENT "FocusRequests: <none>\n";
Vishnu Nairc519ff72021-01-21 08:23:08 -0800206 }
207
Vishnu Nair1dcad982021-02-24 14:38:25 -0800208 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 Nairc519ff72021-01-21 08:23:08 -0800215 }
216 return dump;
217}
218
Vishnu Nair599f1412021-06-21 10:39:58 -0700219void FocusResolver::displayRemoved(int32_t displayId) {
220 mFocusRequestByDisplay.erase(displayId);
221 mLastFocusResultByDisplay.erase(displayId);
222}
223
Vishnu Nairc519ff72021-01-21 08:23:08 -0800224} // namespace android::inputdispatcher