• 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/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,sp<IBinder> forwardingWindowToken)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, sp<IBinder> forwardingWindowToken) {
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             touchedWindow.forwardingWindowToken = forwardingWindowToken;
92             // For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have
93             // downTime set initially. Need to update existing window when a pointer is down for the
94             // window.
95             android::base::Result<void> addResult =
96                     touchedWindow.addTouchingPointers(deviceId, touchingPointers);
97             if (firstDownTimeInTarget) {
98                 touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
99             }
100             return addResult;
101         }
102     }
103     TouchedWindow touchedWindow;
104     touchedWindow.windowHandle = windowHandle;
105     touchedWindow.dispatchMode = dispatchMode;
106     touchedWindow.targetFlags = targetFlags;
107     touchedWindow.forwardingWindowToken = forwardingWindowToken;
108     touchedWindow.addTouchingPointers(deviceId, touchingPointers);
109     if (firstDownTimeInTarget) {
110         touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
111     }
112     windows.push_back(touchedWindow);
113     return {};
114 }
115 
addHoveringPointerToWindow(const sp<WindowInfoHandle> & windowHandle,DeviceId deviceId,const PointerProperties & pointer,float x,float y)116 void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle,
117                                             DeviceId deviceId, const PointerProperties& pointer,
118                                             float x, float y) {
119     for (TouchedWindow& touchedWindow : windows) {
120         if (touchedWindow.windowHandle == windowHandle) {
121             touchedWindow.addHoveringPointer(deviceId, pointer, x, y);
122             return;
123         }
124     }
125 
126     TouchedWindow touchedWindow;
127     touchedWindow.windowHandle = windowHandle;
128     touchedWindow.addHoveringPointer(deviceId, pointer, x, y);
129     windows.push_back(touchedWindow);
130 }
131 
removeWindowByToken(const sp<IBinder> & token)132 void TouchState::removeWindowByToken(const sp<IBinder>& token) {
133     for (size_t i = 0; i < windows.size(); i++) {
134         if (windows[i].windowHandle->getToken() == token) {
135             windows.erase(windows.begin() + i);
136             return;
137         }
138     }
139 }
140 
cancelPointersForWindowsExcept(DeviceId deviceId,std::bitset<MAX_POINTER_ID+1> pointerIds,const sp<IBinder> & token)141 void TouchState::cancelPointersForWindowsExcept(DeviceId deviceId,
142                                                 std::bitset<MAX_POINTER_ID + 1> pointerIds,
143                                                 const sp<IBinder>& token) {
144     std::for_each(windows.begin(), windows.end(), [&](TouchedWindow& w) {
145         if (w.windowHandle->getToken() != token) {
146             w.removeTouchingPointers(deviceId, pointerIds);
147         }
148     });
149     clearWindowsWithoutPointers();
150 }
151 
152 /**
153  * For any pointer that's being pilfered, remove it from all of the other windows that currently
154  * aren't pilfering it. For example, if we determined that pointer 1 is going to both window A and
155  * window B, but window A is currently pilfering pointer 1, then pointer 1 should not go to window
156  * B.
157  */
cancelPointersForNonPilferingWindows()158 void TouchState::cancelPointersForNonPilferingWindows() {
159     // First, find all pointers that are being pilfered, across all windows
160     std::map<DeviceId, std::bitset<MAX_POINTER_ID + 1>> allPilferedPointerIdsByDevice;
161     for (const TouchedWindow& w : windows) {
162         for (const auto& [deviceId, pilferedPointerIds] : w.getPilferingPointers()) {
163             allPilferedPointerIdsByDevice[deviceId] |= pilferedPointerIds;
164         }
165     };
166 
167     // Optimization: most of the time, pilfering does not occur
168     if (allPilferedPointerIdsByDevice.empty()) return;
169 
170     // Now, remove all pointers from every window that's being pilfered by other windows.
171     // For example, if window A is pilfering pointer 1 (only), and window B is pilfering pointer 2
172     // (only), the remove pointer 2 from window A and pointer 1 from window B. Usually, the set of
173     // pilfered pointers will be disjoint across all windows, but there's no reason to cause that
174     // limitation here.
175     for (const auto& [deviceId, allPilferedPointerIds] : allPilferedPointerIdsByDevice) {
176         std::for_each(windows.begin(), windows.end(), [&](TouchedWindow& w) {
177             std::bitset<MAX_POINTER_ID + 1> pilferedByOtherWindows =
178                     w.getPilferingPointers(deviceId) ^ allPilferedPointerIds;
179             // Remove all pointers pilfered by other windows
180             w.removeTouchingPointers(deviceId, pilferedByOtherWindows);
181         });
182     }
183     clearWindowsWithoutPointers();
184 }
185 
getFirstForegroundWindowHandle(DeviceId deviceId) const186 sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle(DeviceId deviceId) const {
187     for (const auto& window : windows) {
188         if (!window.hasTouchingPointers(deviceId)) {
189             continue;
190         }
191         if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
192             return window.windowHandle;
193         }
194     }
195     return nullptr;
196 }
197 
isSlippery(DeviceId deviceId) const198 bool TouchState::isSlippery(DeviceId deviceId) const {
199     // Must have exactly one foreground window.
200     bool haveSlipperyForegroundWindow = false;
201     for (const TouchedWindow& window : windows) {
202         if (!window.hasTouchingPointers(deviceId)) {
203             continue;
204         }
205         if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
206             if (haveSlipperyForegroundWindow ||
207                 !window.windowHandle->getInfo()->inputConfig.test(
208                         WindowInfo::InputConfig::SLIPPERY)) {
209                 return false;
210             }
211             haveSlipperyForegroundWindow = true;
212         }
213     }
214     return haveSlipperyForegroundWindow;
215 }
216 
getWallpaperWindow(DeviceId deviceId) const217 sp<WindowInfoHandle> TouchState::getWallpaperWindow(DeviceId deviceId) const {
218     for (const auto& window : windows) {
219         if (!window.hasTouchingPointers(deviceId)) {
220             continue;
221         }
222         if (window.windowHandle->getInfo()->inputConfig.test(
223                     gui::WindowInfo::InputConfig::IS_WALLPAPER)) {
224             return window.windowHandle;
225         }
226     }
227     return nullptr;
228 }
229 
getTouchedWindow(const sp<WindowInfoHandle> & windowHandle) const230 const TouchedWindow& TouchState::getTouchedWindow(const sp<WindowInfoHandle>& windowHandle) const {
231     auto it = std::find_if(windows.begin(), windows.end(),
232                            [&](const TouchedWindow& w) { return w.windowHandle == windowHandle; });
233     LOG_ALWAYS_FATAL_IF(it == windows.end(), "Could not find %s", windowHandle->getName().c_str());
234     return *it;
235 }
236 
isDown(DeviceId deviceId) const237 bool TouchState::isDown(DeviceId deviceId) const {
238     return std::any_of(windows.begin(), windows.end(), [&deviceId](const TouchedWindow& window) {
239         return window.hasTouchingPointers(deviceId);
240     });
241 }
242 
hasHoveringPointers(DeviceId deviceId) const243 bool TouchState::hasHoveringPointers(DeviceId deviceId) const {
244     return std::any_of(windows.begin(), windows.end(), [&deviceId](const TouchedWindow& window) {
245         return window.hasHoveringPointers(deviceId);
246     });
247 }
248 
hasActiveStylus() const249 bool TouchState::hasActiveStylus() const {
250     return std::any_of(windows.begin(), windows.end(),
251                        [](const TouchedWindow& window) { return window.hasActiveStylus(); });
252 }
253 
getWindowsWithHoveringPointer(DeviceId deviceId,int32_t pointerId) const254 std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(DeviceId deviceId,
255                                                                          int32_t pointerId) const {
256     std::set<sp<WindowInfoHandle>> out;
257     for (const TouchedWindow& window : windows) {
258         if (window.hasHoveringPointer(deviceId, pointerId)) {
259             out.insert(window.windowHandle);
260         }
261     }
262     return out;
263 }
264 
removeHoveringPointer(int32_t hoveringDeviceId,int32_t hoveringPointerId)265 void TouchState::removeHoveringPointer(int32_t hoveringDeviceId, int32_t hoveringPointerId) {
266     for (TouchedWindow& window : windows) {
267         window.removeHoveringPointer(hoveringDeviceId, hoveringPointerId);
268     }
269     clearWindowsWithoutPointers();
270 }
271 
removeAllPointersForDevice(DeviceId deviceId)272 void TouchState::removeAllPointersForDevice(DeviceId deviceId) {
273     for (TouchedWindow& window : windows) {
274         window.removeAllHoveringPointersForDevice(deviceId);
275         window.removeAllTouchingPointersForDevice(deviceId);
276     }
277 
278     clearWindowsWithoutPointers();
279 }
280 
dump() const281 std::string TouchState::dump() const {
282     std::string out;
283     if (!windows.empty()) {
284         out += "  Windows:\n";
285         for (size_t i = 0; i < windows.size(); i++) {
286             const TouchedWindow& touchedWindow = windows[i];
287             out += StringPrintf("    %zu : ", i) + touchedWindow.dump();
288         }
289     } else {
290         out += "  Windows: <none>\n";
291     }
292     return out;
293 }
294 
operator <<(std::ostream & out,const TouchState & state)295 std::ostream& operator<<(std::ostream& out, const TouchState& state) {
296     out << state.dump();
297     return out;
298 }
299 
300 } // namespace android::inputdispatcher
301