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 #include <gtest/gtest.h>
18
19 #include "../FocusResolver.h"
20
21 #define ASSERT_FOCUS_CHANGE(_changes, _oldFocus, _newFocus) \
22 { \
23 ASSERT_TRUE(_changes.has_value()); \
24 ASSERT_EQ(_oldFocus, _changes->oldFocus); \
25 ASSERT_EQ(_newFocus, _changes->newFocus); \
26 }
27
28 // atest inputflinger_tests:FocusResolverTest
29
30 using android::gui::FocusRequest;
31 using android::gui::WindowInfoHandle;
32
33 namespace android::inputdispatcher {
34
35 namespace {
36
37 class FakeWindowHandle : public WindowInfoHandle {
38 public:
FakeWindowHandle(const std::string & name,const sp<IBinder> & token,bool focusable,bool visible)39 FakeWindowHandle(const std::string& name, const sp<IBinder>& token, bool focusable,
40 bool visible) {
41 mInfo.token = token;
42 mInfo.name = name;
43 setFocusable(focusable);
44 setVisible(visible);
45 }
46
setFocusable(bool focusable)47 void setFocusable(bool focusable) {
48 mInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_FOCUSABLE, !focusable);
49 }
setVisible(bool visible)50 void setVisible(bool visible) {
51 mInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visible);
52 }
53 };
54
55 } // namespace
56
TEST(FocusResolverTest,SetFocusedWindow)57 TEST(FocusResolverTest, SetFocusedWindow) {
58 sp<IBinder> focusableWindowToken = sp<BBinder>::make();
59 sp<IBinder> invisibleWindowToken = sp<BBinder>::make();
60 sp<IBinder> unfocusableWindowToken = sp<BBinder>::make();
61 std::vector<sp<WindowInfoHandle>> windows;
62 windows.push_back(sp<FakeWindowHandle>::make("Focusable", focusableWindowToken,
63 /*focusable=*/true, /*visible=*/true));
64 windows.push_back(sp<FakeWindowHandle>::make("Invisible", invisibleWindowToken,
65 /*focusable=*/true, /*visible=*/false));
66 windows.push_back(sp<FakeWindowHandle>::make("unfocusable", unfocusableWindowToken,
67 /*focusable=*/false, /*visible=*/true));
68
69 // focusable window can get focused
70 FocusRequest request;
71 request.displayId = 42;
72 request.token = focusableWindowToken;
73 FocusResolver focusResolver;
74 std::optional<FocusResolver::FocusChanges> changes =
75 focusResolver.setFocusedWindow(request, windows);
76 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
77 ASSERT_EQ(ui::LogicalDisplayId{request.displayId}, changes->displayId);
78
79 // invisible window cannot get focused
80 request.token = invisibleWindowToken;
81 changes = focusResolver.setFocusedWindow(request, windows);
82 ASSERT_EQ(focusableWindowToken, changes->oldFocus);
83 ASSERT_EQ(nullptr, changes->newFocus);
84 ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
85
86 // unfocusableWindowToken window cannot get focused
87 request.token = unfocusableWindowToken;
88 changes = focusResolver.setFocusedWindow(request, windows);
89 ASSERT_FALSE(changes);
90 }
91
TEST(FocusResolverTest,RemoveFocusFromFocusedWindow)92 TEST(FocusResolverTest, RemoveFocusFromFocusedWindow) {
93 sp<IBinder> focusableWindowToken = sp<BBinder>::make();
94 std::vector<sp<WindowInfoHandle>> windows;
95 windows.push_back(sp<FakeWindowHandle>::make("Focusable", focusableWindowToken,
96 /*focusable=*/true, /*visible=*/true));
97
98 FocusRequest request;
99 request.displayId = 42;
100 request.token = focusableWindowToken;
101 FocusResolver focusResolver;
102 // Focusable window gets focus.
103 request.token = focusableWindowToken;
104 std::optional<FocusResolver::FocusChanges> changes =
105 focusResolver.setFocusedWindow(request, windows);
106 ASSERT_FOCUS_CHANGE(changes, nullptr, focusableWindowToken);
107
108 // Window token of a request is null, focus should be revoked.
109 request.token = NULL;
110 changes = focusResolver.setFocusedWindow(request, windows);
111 ASSERT_EQ(focusableWindowToken, changes->oldFocus);
112 ASSERT_EQ(nullptr, changes->newFocus);
113 ASSERT_FOCUS_CHANGE(changes, focusableWindowToken, nullptr);
114 }
115
TEST(FocusResolverTest,SetFocusedMirroredWindow)116 TEST(FocusResolverTest, SetFocusedMirroredWindow) {
117 sp<IBinder> focusableWindowToken = sp<BBinder>::make();
118 sp<IBinder> invisibleWindowToken = sp<BBinder>::make();
119 sp<IBinder> unfocusableWindowToken = sp<BBinder>::make();
120 std::vector<sp<WindowInfoHandle>> windows;
121 windows.push_back(sp<FakeWindowHandle>::make("Mirror1", focusableWindowToken,
122 /*focusable=*/true, /*visible=*/true));
123 windows.push_back(sp<FakeWindowHandle>::make("Mirror1", focusableWindowToken,
124 /*focusable=*/true, /*visible=*/true));
125
126 windows.push_back(sp<FakeWindowHandle>::make("Mirror2Visible", invisibleWindowToken,
127 /*focusable=*/true, /*visible=*/true));
128 windows.push_back(sp<FakeWindowHandle>::make("Mirror2Invisible", invisibleWindowToken,
129 /*focusable=*/true, /*visible=*/false));
130
131 windows.push_back(sp<FakeWindowHandle>::make("Mirror3Focusable", unfocusableWindowToken,
132 /*focusable=*/true, /*visible=*/true));
133 windows.push_back(sp<FakeWindowHandle>::make("Mirror3Unfocusable", unfocusableWindowToken,
134 /*focusable=*/false, /*visible=*/true));
135
136 // mirrored window can get focused
137 FocusRequest request;
138 request.displayId = 42;
139 request.token = focusableWindowToken;
140 FocusResolver focusResolver;
141 std::optional<FocusResolver::FocusChanges> changes =
142 focusResolver.setFocusedWindow(request, windows);
143 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
144
145 // mirrored window with one visible window can get focused
146 request.token = invisibleWindowToken;
147 changes = focusResolver.setFocusedWindow(request, windows);
148 ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ invisibleWindowToken);
149
150 // mirrored window with one or more unfocusable window cannot get focused
151 request.token = unfocusableWindowToken;
152 changes = focusResolver.setFocusedWindow(request, windows);
153 ASSERT_FOCUS_CHANGE(changes, /*from*/ invisibleWindowToken, /*to*/ nullptr);
154 }
155
TEST(FocusResolverTest,FocusTransferToMirror)156 TEST(FocusResolverTest, FocusTransferToMirror) {
157 sp<IBinder> focusableWindowToken = sp<BBinder>::make();
158 auto window = sp<FakeWindowHandle>::make("Window", focusableWindowToken,
159 /*focusable=*/true, /*visible=*/true);
160 auto mirror = sp<FakeWindowHandle>::make("Mirror", focusableWindowToken,
161 /*focusable=*/true, /*visible=*/true);
162
163 FocusRequest request;
164 request.displayId = 42;
165 request.token = focusableWindowToken;
166 FocusResolver focusResolver;
167 std::optional<FocusResolver::FocusChanges> changes =
168 focusResolver.setFocusedWindow(request, {window, mirror});
169 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
170
171 // The mirror window now comes on top, and the focus does not change
172 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId},
173 {mirror, window});
174 ASSERT_FALSE(changes.has_value());
175
176 // The window now comes on top while the mirror is removed, and the focus does not change
177 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, {window});
178 ASSERT_FALSE(changes.has_value());
179
180 // The window is removed but the mirror is on top, and focus does not change
181 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, {mirror});
182 ASSERT_FALSE(changes.has_value());
183
184 // All windows removed
185 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, {});
186 ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
187 }
188
TEST(FocusResolverTest,SetInputWindows)189 TEST(FocusResolverTest, SetInputWindows) {
190 sp<IBinder> focusableWindowToken = sp<BBinder>::make();
191 std::vector<sp<WindowInfoHandle>> windows;
192 sp<FakeWindowHandle> window =
193 sp<FakeWindowHandle>::make("Focusable", focusableWindowToken, /*focusable=*/true,
194 /*visible=*/true);
195 windows.push_back(window);
196
197 // focusable window can get focused
198 FocusRequest request;
199 request.displayId = 42;
200 request.token = focusableWindowToken;
201 FocusResolver focusResolver;
202 std::optional<FocusResolver::FocusChanges> changes =
203 focusResolver.setFocusedWindow(request, windows);
204 ASSERT_EQ(focusableWindowToken, changes->newFocus);
205
206 // When there are no changes to the window, focus does not change
207 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
208 ASSERT_FALSE(changes.has_value());
209
210 // Window visibility changes and the window loses focus
211 window->setVisible(false);
212 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
213 ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
214 }
215
TEST(FocusResolverTest,FocusRequestsCanBePending)216 TEST(FocusResolverTest, FocusRequestsCanBePending) {
217 sp<IBinder> invisibleWindowToken = sp<BBinder>::make();
218 std::vector<sp<WindowInfoHandle>> windows;
219
220 sp<FakeWindowHandle> invisibleWindow =
221 sp<FakeWindowHandle>::make("Invisible", invisibleWindowToken, /*focusable=*/true,
222 /*visible=*/false);
223 windows.push_back(invisibleWindow);
224
225 // invisible window cannot get focused
226 FocusRequest request;
227 request.displayId = 42;
228 request.token = invisibleWindowToken;
229 FocusResolver focusResolver;
230 std::optional<FocusResolver::FocusChanges> changes =
231 focusResolver.setFocusedWindow(request, windows);
232 ASSERT_FALSE(changes);
233
234 // Window visibility changes and the window gets focused
235 invisibleWindow->setVisible(true);
236 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
237 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ invisibleWindowToken);
238 }
239
TEST(FocusResolverTest,FocusRequestsArePersistent)240 TEST(FocusResolverTest, FocusRequestsArePersistent) {
241 sp<IBinder> windowToken = sp<BBinder>::make();
242 std::vector<sp<WindowInfoHandle>> windows;
243
244 sp<FakeWindowHandle> window =
245 sp<FakeWindowHandle>::make("Test Window", windowToken, /*focusable=*/false,
246 /*visible=*/true);
247 windows.push_back(window);
248
249 // non-focusable window cannot get focused
250 FocusRequest request;
251 request.displayId = 42;
252 request.token = windowToken;
253 FocusResolver focusResolver;
254 std::optional<FocusResolver::FocusChanges> changes =
255 focusResolver.setFocusedWindow(request, windows);
256 ASSERT_FALSE(changes);
257
258 // Focusability changes and the window gets focused
259 window->setFocusable(true);
260 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
261 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
262
263 // Visibility changes and the window loses focus
264 window->setVisible(false);
265 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
266 ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
267
268 // Visibility changes and the window gets focused
269 window->setVisible(true);
270 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
271 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
272
273 // Window is gone and the window loses focus
274 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, {});
275 ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
276
277 // Window returns and the window gains focus
278 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
279 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
280 }
281
TEST(FocusResolverTest,FocusTransferTarget)282 TEST(FocusResolverTest, FocusTransferTarget) {
283 sp<IBinder> hostWindowToken = sp<BBinder>::make();
284 std::vector<sp<WindowInfoHandle>> windows;
285
286 sp<FakeWindowHandle> hostWindow =
287 sp<FakeWindowHandle>::make("Host Window", hostWindowToken, /*focusable=*/true,
288 /*visible=*/true);
289 windows.push_back(hostWindow);
290 sp<IBinder> embeddedWindowToken = sp<BBinder>::make();
291 sp<FakeWindowHandle> embeddedWindow =
292 sp<FakeWindowHandle>::make("Embedded Window", embeddedWindowToken, /*focusable=*/false,
293 /*visible=*/true);
294 windows.push_back(embeddedWindow);
295
296 FocusRequest request;
297 request.displayId = 42;
298 request.token = hostWindowToken;
299
300 // Host wants to transfer touch to embedded.
301 hostWindow->editInfo()->focusTransferTarget = embeddedWindowToken;
302
303 FocusResolver focusResolver;
304 std::optional<FocusResolver::FocusChanges> changes =
305 focusResolver.setFocusedWindow(request, windows);
306 // Embedded was not focusable so host gains focus.
307 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ hostWindowToken);
308
309 // Embedded is now focusable so will gain focus
310 embeddedWindow->setFocusable(true);
311 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
312 ASSERT_FOCUS_CHANGE(changes, /*from*/ hostWindowToken, /*to*/ embeddedWindowToken);
313
314 // Embedded is not visible so host will get focus
315 embeddedWindow->setVisible(false);
316 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
317 ASSERT_FOCUS_CHANGE(changes, /*from*/ embeddedWindowToken, /*to*/ hostWindowToken);
318
319 // Embedded is now visible so will get focus
320 embeddedWindow->setVisible(true);
321 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
322 ASSERT_FOCUS_CHANGE(changes, /*from*/ hostWindowToken, /*to*/ embeddedWindowToken);
323
324 // Remove focusTransferTarget from host. Host will gain focus.
325 hostWindow->editInfo()->focusTransferTarget = nullptr;
326 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
327 ASSERT_FOCUS_CHANGE(changes, /*from*/ embeddedWindowToken, /*to*/ hostWindowToken);
328
329 // Set invalid token for focusTransferTarget. Host will remain focus
330 hostWindow->editInfo()->focusTransferTarget = sp<BBinder>::make();
331 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
332 ASSERT_FALSE(changes);
333 }
334
TEST(FocusResolverTest,FocusTransferMultipleInChain)335 TEST(FocusResolverTest, FocusTransferMultipleInChain) {
336 sp<IBinder> hostWindowToken = sp<BBinder>::make();
337 std::vector<sp<WindowInfoHandle>> windows;
338
339 sp<FakeWindowHandle> hostWindow =
340 sp<FakeWindowHandle>::make("Host Window", hostWindowToken, /*focusable=*/true,
341 /*visible=*/true);
342 windows.push_back(hostWindow);
343 sp<IBinder> embeddedWindowToken = sp<BBinder>::make();
344 sp<FakeWindowHandle> embeddedWindow =
345 sp<FakeWindowHandle>::make("Embedded Window", embeddedWindowToken, /*focusable=*/true,
346 /*visible=*/true);
347 windows.push_back(embeddedWindow);
348
349 sp<IBinder> embeddedWindowToken2 = sp<BBinder>::make();
350 sp<FakeWindowHandle> embeddedWindow2 =
351 sp<FakeWindowHandle>::make("Embedded Window2", embeddedWindowToken2, /*focusable=*/true,
352 /*visible=*/true);
353 windows.push_back(embeddedWindow2);
354
355 FocusRequest request;
356 request.displayId = 42;
357 request.token = hostWindowToken;
358
359 hostWindow->editInfo()->focusTransferTarget = embeddedWindowToken;
360 embeddedWindow->editInfo()->focusTransferTarget = embeddedWindowToken2;
361
362 FocusResolver focusResolver;
363 std::optional<FocusResolver::FocusChanges> changes =
364 focusResolver.setFocusedWindow(request, windows);
365 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ embeddedWindowToken2);
366 }
367
TEST(FocusResolverTest,FocusTransferTargetCycle)368 TEST(FocusResolverTest, FocusTransferTargetCycle) {
369 sp<IBinder> hostWindowToken = sp<BBinder>::make();
370 std::vector<sp<WindowInfoHandle>> windows;
371
372 sp<FakeWindowHandle> hostWindow =
373 sp<FakeWindowHandle>::make("Host Window", hostWindowToken, /*focusable=*/true,
374 /*visible=*/true);
375 windows.push_back(hostWindow);
376 sp<IBinder> embeddedWindowToken = sp<BBinder>::make();
377 sp<FakeWindowHandle> embeddedWindow =
378 sp<FakeWindowHandle>::make("Embedded Window", embeddedWindowToken, /*focusable=*/true,
379 /*visible=*/true);
380 windows.push_back(embeddedWindow);
381
382 sp<IBinder> embeddedWindowToken2 = sp<BBinder>::make();
383 sp<FakeWindowHandle> embeddedWindow2 =
384 sp<FakeWindowHandle>::make("Embedded Window2", embeddedWindowToken2, /*focusable=*/true,
385 /*visible=*/true);
386 windows.push_back(embeddedWindow2);
387
388 FocusRequest request;
389 request.displayId = 42;
390 request.token = hostWindowToken;
391
392 hostWindow->editInfo()->focusTransferTarget = embeddedWindowToken;
393 embeddedWindow->editInfo()->focusTransferTarget = embeddedWindowToken2;
394 embeddedWindow2->editInfo()->focusTransferTarget = hostWindowToken;
395
396 FocusResolver focusResolver;
397 std::optional<FocusResolver::FocusChanges> changes =
398 focusResolver.setFocusedWindow(request, windows);
399 // Cycle will be detected and stop right before trying to transfer token to host again.
400 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ embeddedWindowToken2);
401 }
402
TEST(FocusResolverTest,FocusRequestsAreClearedWhenWindowIsRemoved)403 TEST(FocusResolverTest, FocusRequestsAreClearedWhenWindowIsRemoved) {
404 sp<IBinder> windowToken = sp<BBinder>::make();
405 std::vector<sp<WindowInfoHandle>> windows;
406
407 sp<FakeWindowHandle> window =
408 sp<FakeWindowHandle>::make("Test Window", windowToken, /*focusable=*/true,
409 /*visible=*/true);
410 windows.push_back(window);
411
412 FocusRequest request;
413 request.displayId = 42;
414 request.token = windowToken;
415 FocusResolver focusResolver;
416 std::optional<FocusResolver::FocusChanges> changes =
417 focusResolver.setFocusedWindow(request, windows);
418 ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
419 ASSERT_EQ(ui::LogicalDisplayId{request.displayId}, changes->displayId);
420
421 // When a display is removed, all windows are removed from the display
422 // and our focused window loses focus
423 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, {});
424 ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
425 focusResolver.displayRemoved(ui::LogicalDisplayId{request.displayId});
426
427 // When a display is re-added, the window does not get focus since the request was cleared.
428 changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
429 ASSERT_FALSE(changes);
430 }
431
432 } // namespace android::inputdispatcher
433