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