1 /*
2 * Copyright (C) 2019 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 <android-base/logging.h>
18 #include <android-base/stringprintf.h>
19 #include <gui/WindowInfo.h>
20
21 #include "InputTarget.h"
22 #include "TouchState.h"
23
24 using namespace android::ftl::flag_operators;
25 using android::base::StringPrintf;
26 using android::gui::WindowInfo;
27 using android::gui::WindowInfoHandle;
28
29 namespace android::inputdispatcher {
30
reset()31 void TouchState::reset() {
32 *this = TouchState();
33 }
34
hasTouchingPointers(DeviceId deviceId) const35 bool TouchState::hasTouchingPointers(DeviceId deviceId) const {
36 return std::any_of(windows.begin(), windows.end(), [&](const TouchedWindow& window) {
37 return window.hasTouchingPointers(deviceId);
38 });
39 }
40
removeTouchingPointer(DeviceId deviceId,int32_t pointerId)41 void TouchState::removeTouchingPointer(DeviceId deviceId, int32_t pointerId) {
42 for (TouchedWindow& touchedWindow : windows) {
43 touchedWindow.removeTouchingPointer(deviceId, pointerId);
44 }
45 clearWindowsWithoutPointers();
46 }
47
removeTouchingPointerFromWindow(DeviceId deviceId,int32_t pointerId,const sp<android::gui::WindowInfoHandle> & windowHandle)48 void TouchState::removeTouchingPointerFromWindow(
49 DeviceId deviceId, int32_t pointerId,
50 const sp<android::gui::WindowInfoHandle>& windowHandle) {
51 for (TouchedWindow& touchedWindow : windows) {
52 if (touchedWindow.windowHandle == windowHandle) {
53 touchedWindow.removeTouchingPointer(deviceId, pointerId);
54 clearWindowsWithoutPointers();
55 return;
56 }
57 }
58 }
59
clearHoveringPointers(DeviceId deviceId)60 void TouchState::clearHoveringPointers(DeviceId deviceId) {
61 for (TouchedWindow& touchedWindow : windows) {
62 touchedWindow.removeAllHoveringPointersForDevice(deviceId);
63 }
64 clearWindowsWithoutPointers();
65 }
66
clearWindowsWithoutPointers()67 void TouchState::clearWindowsWithoutPointers() {
68 std::erase_if(windows, [](const TouchedWindow& w) {
69 return !w.hasTouchingPointers() && !w.hasHoveringPointers();
70 });
71 }
72
addOrUpdateWindow(const sp<WindowInfoHandle> & windowHandle,InputTarget::DispatchMode dispatchMode,ftl::Flags<InputTarget::Flags> targetFlags,DeviceId deviceId,const std::vector<PointerProperties> & touchingPointers,std::optional<nsecs_t> firstDownTimeInTarget)73 android::base::Result<void> TouchState::addOrUpdateWindow(
74 const sp<WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode,
75 ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
76 const std::vector<PointerProperties>& touchingPointers,
77 std::optional<nsecs_t> firstDownTimeInTarget) {
78 if (touchingPointers.empty()) {
79 LOG(FATAL) << __func__ << "No pointers specified for " << windowHandle->getName();
80 return android::base::Error();
81 }
82 for (TouchedWindow& touchedWindow : windows) {
83 // We do not compare windows by token here because two windows that share the same token
84 // may have a different transform. They will be combined later when we create InputTargets.
85 // At that point, per-pointer window transform will be considered.
86 // An alternative design choice here would have been to compare here by token, but to
87 // store per-pointer transform.
88 if (touchedWindow.windowHandle == windowHandle) {
89 touchedWindow.dispatchMode = dispatchMode;
90 touchedWindow.targetFlags |= targetFlags;
91 // For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have
92 // downTime set initially. Need to update existing window when a pointer is down for the
93 // window.
94 android::base::Result<void> addResult =
95 touchedWindow.addTouchingPointers(deviceId, touchingPointers);
96 if (firstDownTimeInTarget) {
97 touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
98 }
99 return addResult;
100 }
101 }
102 TouchedWindow touchedWindow;
103 touchedWindow.windowHandle = windowHandle;
104 touchedWindow.dispatchMode = dispatchMode;
105 touchedWindow.targetFlags = targetFlags;
106 touchedWindow.addTouchingPointers(deviceId, touchingPointers);
107 if (firstDownTimeInTarget) {
108 touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
109 }
110 windows.push_back(touchedWindow);
111 return {};
112 }
113
addHoveringPointerToWindow(const sp<WindowInfoHandle> & windowHandle,DeviceId deviceId,const PointerProperties & pointer,float x,float y)114 void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle,
115 DeviceId deviceId, const PointerProperties& pointer,
116 float x, float y) {
117 for (TouchedWindow& touchedWindow : windows) {
118 if (touchedWindow.windowHandle == windowHandle) {
119 touchedWindow.addHoveringPointer(deviceId, pointer, x, y);
120 return;
121 }
122 }
123
124 TouchedWindow touchedWindow;
125 touchedWindow.windowHandle = windowHandle;
126 touchedWindow.addHoveringPointer(deviceId, pointer, x, y);
127 windows.push_back(touchedWindow);
128 }
129
removeWindowByToken(const sp<IBinder> & token)130 void TouchState::removeWindowByToken(const sp<IBinder>& token) {
131 for (size_t i = 0; i < windows.size(); i++) {
132 if (windows[i].windowHandle->getToken() == token) {
133 windows.erase(windows.begin() + i);
134 return;
135 }
136 }
137 }
138
cancelPointersForWindowsExcept(DeviceId deviceId,std::bitset<MAX_POINTER_ID+1> pointerIds,const sp<IBinder> & token)139 void TouchState::cancelPointersForWindowsExcept(DeviceId deviceId,
140 std::bitset<MAX_POINTER_ID + 1> pointerIds,
141 const sp<IBinder>& token) {
142 std::for_each(windows.begin(), windows.end(), [&](TouchedWindow& w) {
143 if (w.windowHandle->getToken() != token) {
144 w.removeTouchingPointers(deviceId, pointerIds);
145 }
146 });
147 clearWindowsWithoutPointers();
148 }
149
150 /**
151 * For any pointer that's being pilfered, remove it from all of the other windows that currently
152 * aren't pilfering it. For example, if we determined that pointer 1 is going to both window A and
153 * window B, but window A is currently pilfering pointer 1, then pointer 1 should not go to window
154 * B.
155 */
cancelPointersForNonPilferingWindows()156 void TouchState::cancelPointersForNonPilferingWindows() {
157 // First, find all pointers that are being pilfered, across all windows
158 std::map<DeviceId, std::bitset<MAX_POINTER_ID + 1>> allPilferedPointerIdsByDevice;
159 for (const TouchedWindow& w : windows) {
160 for (const auto& [deviceId, pilferedPointerIds] : w.getPilferingPointers()) {
161 allPilferedPointerIdsByDevice[deviceId] |= pilferedPointerIds;
162 }
163 };
164
165 // Optimization: most of the time, pilfering does not occur
166 if (allPilferedPointerIdsByDevice.empty()) return;
167
168 // Now, remove all pointers from every window that's being pilfered by other windows.
169 // For example, if window A is pilfering pointer 1 (only), and window B is pilfering pointer 2
170 // (only), the remove pointer 2 from window A and pointer 1 from window B. Usually, the set of
171 // pilfered pointers will be disjoint across all windows, but there's no reason to cause that
172 // limitation here.
173 for (const auto& [deviceId, allPilferedPointerIds] : allPilferedPointerIdsByDevice) {
174 std::for_each(windows.begin(), windows.end(), [&](TouchedWindow& w) {
175 std::bitset<MAX_POINTER_ID + 1> pilferedByOtherWindows =
176 w.getPilferingPointers(deviceId) ^ allPilferedPointerIds;
177 // Remove all pointers pilfered by other windows
178 w.removeTouchingPointers(deviceId, pilferedByOtherWindows);
179 });
180 }
181 clearWindowsWithoutPointers();
182 }
183
getFirstForegroundWindowHandle(DeviceId deviceId) const184 sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle(DeviceId deviceId) const {
185 for (const auto& window : windows) {
186 if (!window.hasTouchingPointers(deviceId)) {
187 continue;
188 }
189 if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
190 return window.windowHandle;
191 }
192 }
193 return nullptr;
194 }
195
isSlippery(DeviceId deviceId) const196 bool TouchState::isSlippery(DeviceId deviceId) const {
197 // Must have exactly one foreground window.
198 bool haveSlipperyForegroundWindow = false;
199 for (const TouchedWindow& window : windows) {
200 if (!window.hasTouchingPointers(deviceId)) {
201 continue;
202 }
203 if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
204 if (haveSlipperyForegroundWindow ||
205 !window.windowHandle->getInfo()->inputConfig.test(
206 WindowInfo::InputConfig::SLIPPERY)) {
207 return false;
208 }
209 haveSlipperyForegroundWindow = true;
210 }
211 }
212 return haveSlipperyForegroundWindow;
213 }
214
getWallpaperWindow(DeviceId deviceId) const215 sp<WindowInfoHandle> TouchState::getWallpaperWindow(DeviceId deviceId) const {
216 for (const auto& window : windows) {
217 if (!window.hasTouchingPointers(deviceId)) {
218 continue;
219 }
220 if (window.windowHandle->getInfo()->inputConfig.test(
221 gui::WindowInfo::InputConfig::IS_WALLPAPER)) {
222 return window.windowHandle;
223 }
224 }
225 return nullptr;
226 }
227
getTouchedWindow(const sp<WindowInfoHandle> & windowHandle) const228 const TouchedWindow& TouchState::getTouchedWindow(const sp<WindowInfoHandle>& windowHandle) const {
229 auto it = std::find_if(windows.begin(), windows.end(),
230 [&](const TouchedWindow& w) { return w.windowHandle == windowHandle; });
231 LOG_ALWAYS_FATAL_IF(it == windows.end(), "Could not find %s", windowHandle->getName().c_str());
232 return *it;
233 }
234
isDown(DeviceId deviceId) const235 bool TouchState::isDown(DeviceId deviceId) const {
236 return std::any_of(windows.begin(), windows.end(), [&deviceId](const TouchedWindow& window) {
237 return window.hasTouchingPointers(deviceId);
238 });
239 }
240
hasHoveringPointers(DeviceId deviceId) const241 bool TouchState::hasHoveringPointers(DeviceId deviceId) const {
242 return std::any_of(windows.begin(), windows.end(), [&deviceId](const TouchedWindow& window) {
243 return window.hasHoveringPointers(deviceId);
244 });
245 }
246
hasActiveStylus() const247 bool TouchState::hasActiveStylus() const {
248 return std::any_of(windows.begin(), windows.end(),
249 [](const TouchedWindow& window) { return window.hasActiveStylus(); });
250 }
251
getWindowsWithHoveringPointer(DeviceId deviceId,int32_t pointerId) const252 std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(DeviceId deviceId,
253 int32_t pointerId) const {
254 std::set<sp<WindowInfoHandle>> out;
255 for (const TouchedWindow& window : windows) {
256 if (window.hasHoveringPointer(deviceId, pointerId)) {
257 out.insert(window.windowHandle);
258 }
259 }
260 return out;
261 }
262
removeHoveringPointer(int32_t hoveringDeviceId,int32_t hoveringPointerId)263 void TouchState::removeHoveringPointer(int32_t hoveringDeviceId, int32_t hoveringPointerId) {
264 for (TouchedWindow& window : windows) {
265 window.removeHoveringPointer(hoveringDeviceId, hoveringPointerId);
266 }
267 clearWindowsWithoutPointers();
268 }
269
removeAllPointersForDevice(DeviceId deviceId)270 void TouchState::removeAllPointersForDevice(DeviceId deviceId) {
271 for (TouchedWindow& window : windows) {
272 window.removeAllHoveringPointersForDevice(deviceId);
273 window.removeAllTouchingPointersForDevice(deviceId);
274 }
275
276 clearWindowsWithoutPointers();
277 }
278
dump() const279 std::string TouchState::dump() const {
280 std::string out;
281 if (!windows.empty()) {
282 out += " Windows:\n";
283 for (size_t i = 0; i < windows.size(); i++) {
284 const TouchedWindow& touchedWindow = windows[i];
285 out += StringPrintf(" %zu : ", i) + touchedWindow.dump();
286 }
287 } else {
288 out += " Windows: <none>\n";
289 }
290 return out;
291 }
292
operator <<(std::ostream & out,const TouchState & state)293 std::ostream& operator<<(std::ostream& out, const TouchState& state) {
294 out << state.dump();
295 return out;
296 }
297
298 } // namespace android::inputdispatcher
299