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