InputDispatcher: Refactor focus logic into FocusResolver
Pull out logic that keeps track of the focused window per display and
the logic used to determine focus changes to a new class. Input
Dispatcher will feed in focus requests & window handle changes. It
will then consume any focus changes.
There is no functional changes in this cl. Additional logging has been
added when we drop a focus request.
Test: atest inputflinger_tests
Change-Id: I04c0c38907cca5d6d98405a6a9f7eb7c497fd90f
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
new file mode 100644
index 0000000..ef3dd65
--- /dev/null
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "../FocusResolver.h"
+
+// atest inputflinger_tests:FocusResolverTest
+
+namespace android::inputdispatcher {
+
+class FakeWindowHandle : public InputWindowHandle {
+public:
+ FakeWindowHandle(const std::string& name, const sp<IBinder>& token, bool focusable,
+ bool visible) {
+ mInfo.token = token;
+ mInfo.name = name;
+ mInfo.visible = visible;
+ mInfo.focusable = focusable;
+ }
+
+ bool updateInfo() { return true; }
+ void setFocusable(bool focusable) { mInfo.focusable = focusable; }
+ void setVisible(bool visible) { mInfo.visible = visible; }
+};
+
+TEST(FocusResolverTest, SetFocusedWindow) {
+ sp<IBinder> focusableWindowToken = new BBinder();
+ sp<IBinder> invisibleWindowToken = new BBinder();
+ sp<IBinder> unfocusableWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+ windows.push_back(new FakeWindowHandle("Focusable", focusableWindowToken, true /* focusable */,
+ true /* visible */));
+ windows.push_back(new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */,
+ false /* visible */));
+ windows.push_back(new FakeWindowHandle("unfocusable", unfocusableWindowToken,
+ false /* focusable */, true /* visible */));
+
+ // focusable window can get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = focusableWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(nullptr, changes->oldFocus);
+ ASSERT_EQ(focusableWindowToken, changes->newFocus);
+ ASSERT_EQ(request.displayId, changes->displayId);
+
+ // invisible window cannot get focused
+ request.token = invisibleWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+ ASSERT_EQ(nullptr, changes->newFocus);
+
+ // unfocusableWindowToken window cannot get focused
+ request.token = unfocusableWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FALSE(changes);
+}
+
+TEST(FocusResolverTest, SetFocusedMirroredWindow) {
+ sp<IBinder> focusableWindowToken = new BBinder();
+ sp<IBinder> invisibleWindowToken = new BBinder();
+ sp<IBinder> unfocusableWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+ windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */,
+ true /* visible */));
+ windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */,
+ true /* visible */));
+
+ windows.push_back(new FakeWindowHandle("Mirror2Visible", invisibleWindowToken,
+ true /* focusable */, true /* visible */));
+ windows.push_back(new FakeWindowHandle("Mirror2Invisible", invisibleWindowToken,
+ true /* focusable */, false /* visible */));
+
+ windows.push_back(new FakeWindowHandle("Mirror3Focusable", unfocusableWindowToken,
+ true /* focusable */, true /* visible */));
+ windows.push_back(new FakeWindowHandle("Mirror3Unfocusable", unfocusableWindowToken,
+ false /* focusable */, true /* visible */));
+
+ // mirrored window can get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = focusableWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(nullptr, changes->oldFocus);
+ ASSERT_EQ(focusableWindowToken, changes->newFocus);
+
+ // mirrored window with one visible window can get focused
+ request.token = invisibleWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+ ASSERT_EQ(invisibleWindowToken, changes->newFocus);
+
+ // mirrored window with one or more unfocusable window cannot get focused
+ request.token = unfocusableWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FALSE(changes);
+}
+
+TEST(FocusResolverTest, SetInputWindows) {
+ sp<IBinder> focusableWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+ sp<FakeWindowHandle> window = new FakeWindowHandle("Focusable", focusableWindowToken,
+ true /* focusable */, true /* visible */);
+ windows.push_back(window);
+
+ // focusable window can get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = focusableWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(focusableWindowToken, changes->newFocus);
+
+ // Window visibility changes and the window loses focused
+ window->setVisible(false);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_EQ(nullptr, changes->newFocus);
+ ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+}
+
+TEST(FocusResolverTest, FocusRequestsCanBePending) {
+ sp<IBinder> invisibleWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+
+ sp<FakeWindowHandle> invisibleWindow =
+ new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */,
+ false /* visible */);
+ windows.push_back(invisibleWindow);
+
+ // invisible window cannot get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = invisibleWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FALSE(changes);
+
+ // Window visibility changes and the window gets focused
+ invisibleWindow->setVisible(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_EQ(nullptr, changes->oldFocus);
+ ASSERT_EQ(invisibleWindowToken, changes->newFocus);
+}
+
+} // namespace android::inputdispatcher