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