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