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 "InputState.h"
18
19 #include "InputDispatcher.h"
20
21 namespace android::inputdispatcher {
22
InputState(const IdGenerator & idGenerator)23 InputState::InputState(const IdGenerator& idGenerator) : mIdGenerator(idGenerator) {}
24
~InputState()25 InputState::~InputState() {}
26
isNeutral() const27 bool InputState::isNeutral() const {
28 return mKeyMementos.empty() && mMotionMementos.empty();
29 }
30
isHovering(int32_t deviceId,uint32_t source,int32_t displayId) const31 bool InputState::isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const {
32 for (const MotionMemento& memento : mMotionMementos) {
33 if (memento.deviceId == deviceId && memento.source == source &&
34 memento.displayId == displayId && memento.hovering) {
35 return true;
36 }
37 }
38 return false;
39 }
40
trackKey(const KeyEntry & entry,int32_t action,int32_t flags)41 bool InputState::trackKey(const KeyEntry& entry, int32_t action, int32_t flags) {
42 switch (action) {
43 case AKEY_EVENT_ACTION_UP: {
44 if (entry.flags & AKEY_EVENT_FLAG_FALLBACK) {
45 for (size_t i = 0; i < mFallbackKeys.size();) {
46 if (mFallbackKeys.valueAt(i) == entry.keyCode) {
47 mFallbackKeys.removeItemsAt(i);
48 } else {
49 i += 1;
50 }
51 }
52 }
53 ssize_t index = findKeyMemento(entry);
54 if (index >= 0) {
55 mKeyMementos.erase(mKeyMementos.begin() + index);
56 return true;
57 }
58 /* FIXME: We can't just drop the key up event because that prevents creating
59 * popup windows that are automatically shown when a key is held and then
60 * dismissed when the key is released. The problem is that the popup will
61 * not have received the original key down, so the key up will be considered
62 * to be inconsistent with its observed state. We could perhaps handle this
63 * by synthesizing a key down but that will cause other problems.
64 *
65 * So for now, allow inconsistent key up events to be dispatched.
66 *
67 #if DEBUG_OUTBOUND_EVENT_DETAILS
68 ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, "
69 "keyCode=%d, scanCode=%d",
70 entry.deviceId, entry.source, entry.keyCode, entry.scanCode);
71 #endif
72 return false;
73 */
74 return true;
75 }
76
77 case AKEY_EVENT_ACTION_DOWN: {
78 ssize_t index = findKeyMemento(entry);
79 if (index >= 0) {
80 mKeyMementos.erase(mKeyMementos.begin() + index);
81 }
82 addKeyMemento(entry, flags);
83 return true;
84 }
85
86 default:
87 return true;
88 }
89 }
90
trackMotion(const MotionEntry & entry,int32_t action,int32_t flags)91 bool InputState::trackMotion(const MotionEntry& entry, int32_t action, int32_t flags) {
92 int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
93 switch (actionMasked) {
94 case AMOTION_EVENT_ACTION_UP:
95 case AMOTION_EVENT_ACTION_CANCEL: {
96 ssize_t index = findMotionMemento(entry, false /*hovering*/);
97 if (index >= 0) {
98 mMotionMementos.erase(mMotionMementos.begin() + index);
99 return true;
100 }
101 #if DEBUG_OUTBOUND_EVENT_DETAILS
102 ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, "
103 "displayId=%" PRId32 ", actionMasked=%d",
104 entry.deviceId, entry.source, entry.displayId, actionMasked);
105 #endif
106 return false;
107 }
108
109 case AMOTION_EVENT_ACTION_DOWN: {
110 ssize_t index = findMotionMemento(entry, false /*hovering*/);
111 if (index >= 0) {
112 mMotionMementos.erase(mMotionMementos.begin() + index);
113 }
114 addMotionMemento(entry, flags, false /*hovering*/);
115 return true;
116 }
117
118 case AMOTION_EVENT_ACTION_POINTER_UP:
119 case AMOTION_EVENT_ACTION_POINTER_DOWN:
120 case AMOTION_EVENT_ACTION_MOVE: {
121 if (entry.source & AINPUT_SOURCE_CLASS_NAVIGATION) {
122 // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need
123 // to generate cancellation events for these since they're based in relative rather
124 // than absolute units.
125 return true;
126 }
127
128 ssize_t index = findMotionMemento(entry, false /*hovering*/);
129
130 if (entry.source & AINPUT_SOURCE_CLASS_JOYSTICK) {
131 // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all
132 // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral.
133 // Any other value and we need to track the motion so we can send cancellation
134 // events for anything generating fallback events (e.g. DPad keys for joystick
135 // movements).
136 if (index >= 0) {
137 if (entry.pointerCoords[0].isEmpty()) {
138 mMotionMementos.erase(mMotionMementos.begin() + index);
139 } else {
140 MotionMemento& memento = mMotionMementos[index];
141 memento.setPointers(entry);
142 }
143 } else if (!entry.pointerCoords[0].isEmpty()) {
144 addMotionMemento(entry, flags, false /*hovering*/);
145 }
146
147 // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP.
148 return true;
149 }
150
151 if (index >= 0) {
152 MotionMemento& memento = mMotionMementos[index];
153 if (memento.firstNewPointerIdx < 0) {
154 memento.setPointers(entry);
155 return true;
156 }
157 }
158 #if DEBUG_OUTBOUND_EVENT_DETAILS
159 ALOGD("Dropping inconsistent motion pointer up/down or move event: "
160 "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d",
161 entry.deviceId, entry.source, entry.displayId, actionMasked);
162 #endif
163 return false;
164 }
165
166 case AMOTION_EVENT_ACTION_HOVER_EXIT: {
167 ssize_t index = findMotionMemento(entry, true /*hovering*/);
168 if (index >= 0) {
169 mMotionMementos.erase(mMotionMementos.begin() + index);
170 return true;
171 }
172 #if DEBUG_OUTBOUND_EVENT_DETAILS
173 ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, "
174 "displayId=%" PRId32,
175 entry.deviceId, entry.source, entry.displayId);
176 #endif
177 return false;
178 }
179
180 case AMOTION_EVENT_ACTION_HOVER_ENTER:
181 case AMOTION_EVENT_ACTION_HOVER_MOVE: {
182 ssize_t index = findMotionMemento(entry, true /*hovering*/);
183 if (index >= 0) {
184 mMotionMementos.erase(mMotionMementos.begin() + index);
185 }
186 addMotionMemento(entry, flags, true /*hovering*/);
187 return true;
188 }
189
190 default:
191 return true;
192 }
193 }
194
findKeyMemento(const KeyEntry & entry) const195 ssize_t InputState::findKeyMemento(const KeyEntry& entry) const {
196 for (size_t i = 0; i < mKeyMementos.size(); i++) {
197 const KeyMemento& memento = mKeyMementos[i];
198 if (memento.deviceId == entry.deviceId && memento.source == entry.source &&
199 memento.displayId == entry.displayId && memento.keyCode == entry.keyCode &&
200 memento.scanCode == entry.scanCode) {
201 return i;
202 }
203 }
204 return -1;
205 }
206
findMotionMemento(const MotionEntry & entry,bool hovering) const207 ssize_t InputState::findMotionMemento(const MotionEntry& entry, bool hovering) const {
208 for (size_t i = 0; i < mMotionMementos.size(); i++) {
209 const MotionMemento& memento = mMotionMementos[i];
210 if (memento.deviceId == entry.deviceId && memento.source == entry.source &&
211 memento.displayId == entry.displayId && memento.hovering == hovering) {
212 return i;
213 }
214 }
215 return -1;
216 }
217
addKeyMemento(const KeyEntry & entry,int32_t flags)218 void InputState::addKeyMemento(const KeyEntry& entry, int32_t flags) {
219 KeyMemento memento;
220 memento.deviceId = entry.deviceId;
221 memento.source = entry.source;
222 memento.displayId = entry.displayId;
223 memento.keyCode = entry.keyCode;
224 memento.scanCode = entry.scanCode;
225 memento.metaState = entry.metaState;
226 memento.flags = flags;
227 memento.downTime = entry.downTime;
228 memento.policyFlags = entry.policyFlags;
229 mKeyMementos.push_back(memento);
230 }
231
addMotionMemento(const MotionEntry & entry,int32_t flags,bool hovering)232 void InputState::addMotionMemento(const MotionEntry& entry, int32_t flags, bool hovering) {
233 MotionMemento memento;
234 memento.deviceId = entry.deviceId;
235 memento.source = entry.source;
236 memento.displayId = entry.displayId;
237 memento.flags = flags;
238 memento.xPrecision = entry.xPrecision;
239 memento.yPrecision = entry.yPrecision;
240 memento.xCursorPosition = entry.xCursorPosition;
241 memento.yCursorPosition = entry.yCursorPosition;
242 memento.downTime = entry.downTime;
243 memento.setPointers(entry);
244 memento.hovering = hovering;
245 memento.policyFlags = entry.policyFlags;
246 mMotionMementos.push_back(memento);
247 }
248
setPointers(const MotionEntry & entry)249 void InputState::MotionMemento::setPointers(const MotionEntry& entry) {
250 pointerCount = entry.pointerCount;
251 for (uint32_t i = 0; i < entry.pointerCount; i++) {
252 pointerProperties[i].copyFrom(entry.pointerProperties[i]);
253 pointerCoords[i].copyFrom(entry.pointerCoords[i]);
254 }
255 }
256
mergePointerStateTo(MotionMemento & other) const257 void InputState::MotionMemento::mergePointerStateTo(MotionMemento& other) const {
258 for (uint32_t i = 0; i < pointerCount; i++) {
259 if (other.firstNewPointerIdx < 0) {
260 other.firstNewPointerIdx = other.pointerCount;
261 }
262 other.pointerProperties[other.pointerCount].copyFrom(pointerProperties[i]);
263 other.pointerCoords[other.pointerCount].copyFrom(pointerCoords[i]);
264 other.pointerCount++;
265 }
266 }
267
synthesizeCancelationEvents(nsecs_t currentTime,const CancelationOptions & options)268 std::vector<EventEntry*> InputState::synthesizeCancelationEvents(
269 nsecs_t currentTime, const CancelationOptions& options) {
270 std::vector<EventEntry*> events;
271 for (KeyMemento& memento : mKeyMementos) {
272 if (shouldCancelKey(memento, options)) {
273 events.push_back(new KeyEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
274 memento.source, memento.displayId, memento.policyFlags,
275 AKEY_EVENT_ACTION_UP,
276 memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode,
277 memento.scanCode, memento.metaState, 0 /*repeatCount*/,
278 memento.downTime));
279 }
280 }
281
282 for (const MotionMemento& memento : mMotionMementos) {
283 if (shouldCancelMotion(memento, options)) {
284 const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT
285 : AMOTION_EVENT_ACTION_CANCEL;
286 events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
287 memento.source, memento.displayId, memento.policyFlags,
288 action, 0 /*actionButton*/, memento.flags, AMETA_NONE,
289 0 /*buttonState*/, MotionClassification::NONE,
290 AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
291 memento.yPrecision, memento.xCursorPosition,
292 memento.yCursorPosition, memento.downTime,
293 memento.pointerCount, memento.pointerProperties,
294 memento.pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/));
295 }
296 }
297 return events;
298 }
299
synthesizePointerDownEvents(nsecs_t currentTime)300 std::vector<EventEntry*> InputState::synthesizePointerDownEvents(nsecs_t currentTime) {
301 std::vector<EventEntry*> events;
302 for (MotionMemento& memento : mMotionMementos) {
303 if (!(memento.source & AINPUT_SOURCE_CLASS_POINTER)) {
304 continue;
305 }
306
307 if (memento.firstNewPointerIdx < 0) {
308 continue;
309 }
310
311 uint32_t pointerCount = 0;
312 PointerProperties pointerProperties[MAX_POINTERS];
313 PointerCoords pointerCoords[MAX_POINTERS];
314
315 // We will deliver all pointers the target already knows about
316 for (uint32_t i = 0; i < static_cast<uint32_t>(memento.firstNewPointerIdx); i++) {
317 pointerProperties[i].copyFrom(memento.pointerProperties[i]);
318 pointerCoords[i].copyFrom(memento.pointerCoords[i]);
319 pointerCount++;
320 }
321
322 // We will send explicit events for all pointers the target doesn't know about
323 for (uint32_t i = static_cast<uint32_t>(memento.firstNewPointerIdx);
324 i < memento.pointerCount; i++) {
325
326 pointerProperties[i].copyFrom(memento.pointerProperties[i]);
327 pointerCoords[i].copyFrom(memento.pointerCoords[i]);
328 pointerCount++;
329
330 // Down only if the first pointer, pointer down otherwise
331 const int32_t action = (pointerCount <= 1)
332 ? AMOTION_EVENT_ACTION_DOWN
333 : AMOTION_EVENT_ACTION_POINTER_DOWN
334 | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
335
336 events.push_back(new MotionEntry(mIdGenerator.nextId(), currentTime, memento.deviceId,
337 memento.source, memento.displayId, memento.policyFlags,
338 action, 0 /*actionButton*/, memento.flags, AMETA_NONE,
339 0 /*buttonState*/, MotionClassification::NONE,
340 AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
341 memento.yPrecision, memento.xCursorPosition,
342 memento.yCursorPosition, memento.downTime,
343 pointerCount, pointerProperties, pointerCoords,
344 0 /*xOffset*/, 0 /*yOffset*/));
345 }
346
347 memento.firstNewPointerIdx = INVALID_POINTER_INDEX;
348 }
349
350 return events;
351 }
352
clear()353 void InputState::clear() {
354 mKeyMementos.clear();
355 mMotionMementos.clear();
356 mFallbackKeys.clear();
357 }
358
mergePointerStateTo(InputState & other)359 void InputState::mergePointerStateTo(InputState& other) {
360 for (size_t i = 0; i < mMotionMementos.size(); i++) {
361 MotionMemento& memento = mMotionMementos[i];
362 // Since we support split pointers we need to merge touch events
363 // from the same source + device + screen.
364 if (memento.source & AINPUT_SOURCE_CLASS_POINTER) {
365 bool merged = false;
366 for (size_t j = 0; j < other.mMotionMementos.size(); j++) {
367 MotionMemento& otherMemento = other.mMotionMementos[j];
368 if (memento.deviceId == otherMemento.deviceId &&
369 memento.source == otherMemento.source &&
370 memento.displayId == otherMemento.displayId) {
371 memento.mergePointerStateTo(otherMemento);
372 merged = true;
373 break;
374 }
375 }
376 if (!merged) {
377 memento.firstNewPointerIdx = 0;
378 other.mMotionMementos.push_back(memento);
379 }
380 }
381 }
382 }
383
getFallbackKey(int32_t originalKeyCode)384 int32_t InputState::getFallbackKey(int32_t originalKeyCode) {
385 ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
386 return index >= 0 ? mFallbackKeys.valueAt(index) : -1;
387 }
388
setFallbackKey(int32_t originalKeyCode,int32_t fallbackKeyCode)389 void InputState::setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode) {
390 ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode);
391 if (index >= 0) {
392 mFallbackKeys.replaceValueAt(index, fallbackKeyCode);
393 } else {
394 mFallbackKeys.add(originalKeyCode, fallbackKeyCode);
395 }
396 }
397
removeFallbackKey(int32_t originalKeyCode)398 void InputState::removeFallbackKey(int32_t originalKeyCode) {
399 mFallbackKeys.removeItem(originalKeyCode);
400 }
401
shouldCancelKey(const KeyMemento & memento,const CancelationOptions & options)402 bool InputState::shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options) {
403 if (options.keyCode && memento.keyCode != options.keyCode.value()) {
404 return false;
405 }
406
407 if (options.deviceId && memento.deviceId != options.deviceId.value()) {
408 return false;
409 }
410
411 if (options.displayId && memento.displayId != options.displayId.value()) {
412 return false;
413 }
414
415 switch (options.mode) {
416 case CancelationOptions::CANCEL_ALL_EVENTS:
417 case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
418 return true;
419 case CancelationOptions::CANCEL_FALLBACK_EVENTS:
420 return memento.flags & AKEY_EVENT_FLAG_FALLBACK;
421 default:
422 return false;
423 }
424 }
425
shouldCancelMotion(const MotionMemento & memento,const CancelationOptions & options)426 bool InputState::shouldCancelMotion(const MotionMemento& memento,
427 const CancelationOptions& options) {
428 if (options.deviceId && memento.deviceId != options.deviceId.value()) {
429 return false;
430 }
431
432 if (options.displayId && memento.displayId != options.displayId.value()) {
433 return false;
434 }
435
436 switch (options.mode) {
437 case CancelationOptions::CANCEL_ALL_EVENTS:
438 return true;
439 case CancelationOptions::CANCEL_POINTER_EVENTS:
440 return memento.source & AINPUT_SOURCE_CLASS_POINTER;
441 case CancelationOptions::CANCEL_NON_POINTER_EVENTS:
442 return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
443 default:
444 return false;
445 }
446 }
447
448 } // namespace android::inputdispatcher
449