blob: 64dbb8ceb4111131011cbef015f288e3927efe7b [file] [log] [blame]
Siarhei Vishniakouba0a8752021-09-14 14:43:25 -07001/*
2 * Copyright (C) 2022 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#define LOG_TAG "UnwantedInteractionBlocker"
18#include "UnwantedInteractionBlocker.h"
19
20#include <android-base/stringprintf.h>
21#include <inttypes.h>
22#include <linux/input-event-codes.h>
23#include <linux/input.h>
24#include <server_configurable_flags/get_flags.h>
25
26#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h"
27#include "ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h"
28
29using android::base::StringPrintf;
30
31namespace android {
32
33// Category (=namespace) name for the input settings that are applied at boot time
34static const char* INPUT_NATIVE_BOOT = "input_native_boot";
35/**
36 * Feature flag name. This flag determines whether palm rejection is enabled. To enable, specify
37 * 'true' (not case sensitive) or '1'. To disable, specify any other value.
38 */
39static const char* PALM_REJECTION_ENABLED = "palm_rejection_enabled";
40
41static std::string toLower(std::string s) {
42 std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });
43 return s;
44}
45
46static bool isFromTouchscreen(int32_t source) {
47 return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN);
48}
49
50static ::base::TimeTicks toChromeTimestamp(nsecs_t eventTime) {
51 return ::base::TimeTicks::UnixEpoch() +
52 ::base::Milliseconds(static_cast<float>(ns2ms(eventTime)));
53}
54
55/**
56 * Return true if palm rejection is enabled via the server configurable flags. Return false
57 * otherwise.
58 */
59static bool isPalmRejectionEnabled() {
60 std::string value = toLower(
61 server_configurable_flags::GetServerConfigurableFlag(INPUT_NATIVE_BOOT,
62 PALM_REJECTION_ENABLED, "false"));
63 if (value == "true" || value == "1") {
64 return true;
65 }
66 return false;
67}
68
69static int getLinuxToolType(int32_t toolType) {
70 switch (toolType) {
71 case AMOTION_EVENT_TOOL_TYPE_FINGER:
72 return MT_TOOL_FINGER;
73 case AMOTION_EVENT_TOOL_TYPE_STYLUS:
74 return MT_TOOL_PEN;
75 case AMOTION_EVENT_TOOL_TYPE_PALM:
76 return MT_TOOL_PALM;
77 }
78 ALOGW("Got tool type %" PRId32 ", converting to MT_TOOL_FINGER", toolType);
79 return MT_TOOL_FINGER;
80}
81
82static std::string addPrefix(std::string str, const std::string& prefix) {
83 std::stringstream ss;
84 bool newLineStarted = true;
85 for (const auto& ch : str) {
86 if (newLineStarted) {
87 ss << prefix;
88 newLineStarted = false;
89 }
90 if (ch == '\n') {
91 newLineStarted = true;
92 }
93 ss << ch;
94 }
95 return ss.str();
96}
97
98template <typename T>
99static std::string dumpSet(const std::set<T>& v) {
100 static_assert(std::is_integral<T>::value, "Only integral types can be printed.");
101 std::string out;
102 for (const T& entry : v) {
103 out += out.empty() ? "{" : ", ";
104 out += android::base::StringPrintf("%i", entry);
105 }
106 return out.empty() ? "{}" : (out + "}");
107}
108
109template <typename K, typename V>
110static std::string dumpMap(const std::map<K, V>& map) {
111 static_assert(std::is_integral<K>::value, "Keys should have integral type to be printed.");
112 static_assert(std::is_integral<V>::value, "Values should have integral type to be printed.");
113 std::string out;
114 for (const auto& [k, v] : map) {
115 if (!out.empty()) {
116 out += "\n";
117 }
118 out += android::base::StringPrintf("%i : %i", static_cast<int>(k), static_cast<int>(v));
119 }
120 return out;
121}
122
123static std::string dumpDeviceInfo(const AndroidPalmFilterDeviceInfo& info) {
124 std::string out;
125 out += StringPrintf("max_x = %.2f\n", info.max_x);
126 out += StringPrintf("max_y = %.2f\n", info.max_y);
127 out += StringPrintf("x_res = %.2f\n", info.x_res);
128 out += StringPrintf("y_res = %.2f\n", info.y_res);
129 out += StringPrintf("major_radius_res = %.2f\n", info.major_radius_res);
130 out += StringPrintf("minor_radius_res = %.2f\n", info.minor_radius_res);
131 out += StringPrintf("minor_radius_supported = %s\n",
132 info.minor_radius_supported ? "true" : "false");
133 out += StringPrintf("touch_major_res = %" PRId32 "\n", info.touch_major_res);
134 out += StringPrintf("touch_minor_res = %" PRId32 "\n", info.touch_minor_res);
135 return out;
136}
137
138static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) {
139 for (size_t i = 0; i < args.pointerCount; i++) {
140 if (pointerId == args.pointerProperties[i].id) {
141 return AMOTION_EVENT_ACTION_POINTER_UP |
142 (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
143 }
144 }
145 LOG_ALWAYS_FATAL("Can't find pointerId %" PRId32 " in %s", pointerId, args.dump().c_str());
146}
147
148/**
149 * Find the action for individual pointer at the given pointer index.
150 * This is always equal to MotionEvent::getActionMasked, except for
151 * POINTER_UP or POINTER_DOWN events. For example, in a POINTER_UP event, the action for
152 * the active pointer is ACTION_POINTER_UP, while the action for the other pointers is ACTION_MOVE.
153 */
154static int32_t resolveActionForPointer(uint8_t pointerIndex, int32_t action) {
155 const int32_t actionMasked = MotionEvent::getActionMasked(action);
156 if (actionMasked != AMOTION_EVENT_ACTION_POINTER_DOWN &&
157 actionMasked != AMOTION_EVENT_ACTION_POINTER_UP) {
158 return actionMasked;
159 }
160 // This is a POINTER_DOWN or POINTER_UP event
161 const uint8_t actionIndex = MotionEvent::getActionIndex(action);
162 if (pointerIndex == actionIndex) {
163 return actionMasked;
164 }
165 // When POINTER_DOWN or POINTER_UP happens, it's actually a MOVE for all of the other
166 // pointers
167 return AMOTION_EVENT_ACTION_MOVE;
168}
169
170static const char* toString(bool value) {
171 return value ? "true" : "false";
172}
173
174std::string toString(const ::ui::InProgressTouchEvdev& touch) {
175 return StringPrintf("x=%.1f, y=%.1f, tracking_id=%i, slot=%zu,"
176 " pressure=%.1f, major=%i, minor=%i, "
177 "tool_type=%i, altered=%s, was_touching=%s, touching=%s",
178 touch.x, touch.y, touch.tracking_id, touch.slot, touch.pressure,
179 touch.major, touch.minor, touch.tool_type, toString(touch.altered),
180 toString(touch.was_touching), toString(touch.touching));
181}
182
183/**
184 * Remove the data for the provided pointers from the args. The pointers are identified by their
185 * pointerId, not by the index inside the array.
186 * Return the new NotifyMotionArgs struct that has the remaining pointers.
187 * The only fields that may be different in the returned args from the provided args are:
188 * - action
189 * - pointerCount
190 * - pointerProperties
191 * - pointerCoords
192 * Action might change because it contains a pointer index. If another pointer is removed, the
193 * active pointer index would be shifted.
194 * Do not call this function for events with POINTER_UP or POINTER_DOWN events when removed pointer
195 * id is the acting pointer id.
196 *
197 * @param args the args from which the pointers should be removed
198 * @param pointerIds the pointer ids of the pointers that should be removed
199 */
200NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args,
201 const std::set<int32_t>& pointerIds) {
202 const uint8_t actionIndex = MotionEvent::getActionIndex(args.action);
203 const int32_t actionMasked = MotionEvent::getActionMasked(args.action);
204 const bool isPointerUpOrDownAction = actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN ||
205 actionMasked == AMOTION_EVENT_ACTION_POINTER_UP;
206
207 NotifyMotionArgs newArgs{args};
208 newArgs.pointerCount = 0;
209 int32_t newActionIndex = 0;
210 for (uint32_t i = 0; i < args.pointerCount; i++) {
211 const int32_t pointerId = args.pointerProperties[i].id;
212 if (pointerIds.find(pointerId) != pointerIds.end()) {
213 // skip this pointer
214 if (isPointerUpOrDownAction && i == actionIndex) {
215 // The active pointer is being removed, so the action is no longer valid.
216 // Set the action to 'UNKNOWN' here. The caller is responsible for updating this
217 // action later to a proper value.
218 newArgs.action = ACTION_UNKNOWN;
219 }
220 continue;
221 }
222 newArgs.pointerProperties[newArgs.pointerCount].copyFrom(args.pointerProperties[i]);
223 newArgs.pointerCoords[newArgs.pointerCount].copyFrom(args.pointerCoords[i]);
224 if (i == actionIndex) {
225 newActionIndex = newArgs.pointerCount;
226 }
227 newArgs.pointerCount++;
228 }
229 // Update POINTER_DOWN or POINTER_UP actions
230 if (isPointerUpOrDownAction && newArgs.action != ACTION_UNKNOWN) {
231 newArgs.action =
232 actionMasked | (newActionIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
233 // Convert POINTER_DOWN and POINTER_UP to DOWN and UP if there's only 1 pointer remaining
234 if (newArgs.pointerCount == 1) {
235 if (actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN) {
236 newArgs.action = AMOTION_EVENT_ACTION_DOWN;
237 } else if (actionMasked == AMOTION_EVENT_ACTION_POINTER_UP) {
238 newArgs.action = AMOTION_EVENT_ACTION_UP;
239 }
240 }
241 }
242 return newArgs;
243}
244
245std::optional<AndroidPalmFilterDeviceInfo> createPalmFilterDeviceInfo(
246 const InputDeviceInfo& deviceInfo) {
247 if (!isFromTouchscreen(deviceInfo.getSources())) {
248 return std::nullopt;
249 }
250 AndroidPalmFilterDeviceInfo out;
251 const InputDeviceInfo::MotionRange* axisX =
252 deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN);
253 if (axisX != nullptr) {
254 out.max_x = axisX->max;
255 out.x_res = axisX->resolution;
256 } else {
257 ALOGW("Palm rejection is disabled for %s because AXIS_X is not supported",
258 deviceInfo.getDisplayName().c_str());
259 return std::nullopt;
260 }
261 const InputDeviceInfo::MotionRange* axisY =
262 deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN);
263 if (axisY != nullptr) {
264 out.max_y = axisY->max;
265 out.y_res = axisY->resolution;
266 } else {
267 ALOGW("Palm rejection is disabled for %s because AXIS_Y is not supported",
268 deviceInfo.getDisplayName().c_str());
269 return std::nullopt;
270 }
271 const InputDeviceInfo::MotionRange* axisMajor =
272 deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, AINPUT_SOURCE_TOUCHSCREEN);
273 if (axisMajor != nullptr) {
274 out.major_radius_res = axisMajor->resolution;
275 out.touch_major_res = axisMajor->resolution;
276 } else {
277 return std::nullopt;
278 }
279 const InputDeviceInfo::MotionRange* axisMinor =
280 deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_TOUCH_MINOR, AINPUT_SOURCE_TOUCHSCREEN);
281 if (axisMinor != nullptr) {
282 out.minor_radius_res = axisMinor->resolution;
283 out.touch_minor_res = axisMinor->resolution;
284 out.minor_radius_supported = true;
285 } else {
286 out.minor_radius_supported = false;
287 }
288
289 return out;
290}
291
292/**
293 * Synthesize CANCEL events for any new pointers that should be canceled, while removing pointers
294 * that have already been canceled.
295 * The flow of the function is as follows:
296 * 1. Remove all already canceled pointers
297 * 2. Cancel all newly suppressed pointers
298 * 3. Decide what to do with the current event : keep it, or drop it
299 * The pointers can never be "unsuppressed": once a pointer is canceled, it will never become valid.
300 */
301std::vector<NotifyMotionArgs> cancelSuppressedPointers(
302 const NotifyMotionArgs& args, const std::set<int32_t>& oldSuppressedPointerIds,
303 const std::set<int32_t>& newSuppressedPointerIds) {
304 LOG_ALWAYS_FATAL_IF(args.pointerCount == 0, "0 pointers in %s", args.dump().c_str());
305
306 // First, let's remove the old suppressed pointers. They've already been canceled previously.
307 NotifyMotionArgs oldArgs = removePointerIds(args, oldSuppressedPointerIds);
308
309 // Cancel any newly suppressed pointers.
310 std::vector<NotifyMotionArgs> out;
311 const int32_t activePointerId =
312 args.pointerProperties[MotionEvent::getActionIndex(args.action)].id;
313 const int32_t actionMasked = MotionEvent::getActionMasked(args.action);
314 // We will iteratively remove pointers from 'removedArgs'.
315 NotifyMotionArgs removedArgs{oldArgs};
316 for (uint32_t i = 0; i < oldArgs.pointerCount; i++) {
317 const int32_t pointerId = oldArgs.pointerProperties[i].id;
318 if (newSuppressedPointerIds.find(pointerId) == newSuppressedPointerIds.end()) {
319 // This is a pointer that should not be canceled. Move on.
320 continue;
321 }
322 if (pointerId == activePointerId && actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN) {
323 // Remove this pointer, but don't cancel it. We'll just not send the POINTER_DOWN event
324 removedArgs = removePointerIds(removedArgs, {pointerId});
325 continue;
326 }
327
328 if (removedArgs.pointerCount == 1) {
329 // We are about to remove the last pointer, which means there will be no more gesture
330 // remaining. This is identical to canceling all pointers, so just send a single CANCEL
331 // event, without any of the preceding POINTER_UP with FLAG_CANCELED events.
332 oldArgs.flags |= AMOTION_EVENT_FLAG_CANCELED;
333 oldArgs.action = AMOTION_EVENT_ACTION_CANCEL;
334 return {oldArgs};
335 }
336 // Cancel the current pointer
337 out.push_back(removedArgs);
338 out.back().flags |= AMOTION_EVENT_FLAG_CANCELED;
339 out.back().action = getActionUpForPointerId(out.back(), pointerId);
340
341 // Remove the newly canceled pointer from the args
342 removedArgs = removePointerIds(removedArgs, {pointerId});
343 }
344
345 // Now 'removedArgs' contains only pointers that are valid.
346 if (removedArgs.pointerCount <= 0 || removedArgs.action == ACTION_UNKNOWN) {
347 return out;
348 }
349 out.push_back(removedArgs);
350 return out;
351}
352
353UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& listener)
354 : UnwantedInteractionBlocker(listener, isPalmRejectionEnabled()){};
355
356UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& listener,
357 bool enablePalmRejection)
358 : mListener(listener), mEnablePalmRejection(enablePalmRejection) {}
359
360void UnwantedInteractionBlocker::notifyConfigurationChanged(
361 const NotifyConfigurationChangedArgs* args) {
362 mListener.notifyConfigurationChanged(args);
363}
364
365void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs* args) {
366 mListener.notifyKey(args);
367}
368
369void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) {
370 auto it = mPalmRejectors.find(args->deviceId);
371 const bool sendToPalmRejector = it != mPalmRejectors.end() && isFromTouchscreen(args->source);
372 if (!sendToPalmRejector) {
373 mListener.notifyMotion(args);
374 return;
375 }
376
377 const std::vector<NotifyMotionArgs> newMotions = it->second.processMotion(*args);
378 for (const NotifyMotionArgs& newArgs : newMotions) {
379 mListener.notifyMotion(&newArgs);
380 }
381}
382
383void UnwantedInteractionBlocker::notifySwitch(const NotifySwitchArgs* args) {
384 mListener.notifySwitch(args);
385}
386
387void UnwantedInteractionBlocker::notifySensor(const NotifySensorArgs* args) {
388 mListener.notifySensor(args);
389}
390
391void UnwantedInteractionBlocker::notifyVibratorState(const NotifyVibratorStateArgs* args) {
392 mListener.notifyVibratorState(args);
393}
394void UnwantedInteractionBlocker::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
395 auto it = mPalmRejectors.find(args->deviceId);
396 if (it != mPalmRejectors.end()) {
397 AndroidPalmFilterDeviceInfo info = it->second.getPalmFilterDeviceInfo();
398 // Re-create the object instead of resetting it
399 mPalmRejectors.erase(it);
400 mPalmRejectors.emplace(args->deviceId, info);
401 }
402 mListener.notifyDeviceReset(args);
403}
404
405void UnwantedInteractionBlocker::notifyPointerCaptureChanged(
406 const NotifyPointerCaptureChangedArgs* args) {
407 mListener.notifyPointerCaptureChanged(args);
408}
409
410void UnwantedInteractionBlocker::notifyInputDevicesChanged(
411 const std::vector<InputDeviceInfo>& inputDevices) {
412 if (!mEnablePalmRejection) {
413 // Palm rejection is disabled. Don't create any palm rejector objects.
414 return;
415 }
416
417 // Let's see which of the existing devices didn't change, so that we can keep them
418 // and prevent event stream disruption
419 std::set<int32_t /*deviceId*/> devicesToKeep;
420 for (const InputDeviceInfo& device : inputDevices) {
421 std::optional<AndroidPalmFilterDeviceInfo> info = createPalmFilterDeviceInfo(device);
422 if (!info) {
423 continue;
424 }
425
426 auto [it, emplaced] = mPalmRejectors.try_emplace(device.getId(), *info);
427 if (!emplaced && *info != it->second.getPalmFilterDeviceInfo()) {
428 // Re-create the PalmRejector because the device info has changed.
429 mPalmRejectors.erase(it);
430 mPalmRejectors.emplace(device.getId(), *info);
431 }
432 devicesToKeep.insert(device.getId());
433 }
434 // Delete all devices that we don't need to keep
435 std::erase_if(mPalmRejectors, [&devicesToKeep](const auto& item) {
436 auto const& [deviceId, _] = item;
437 return devicesToKeep.find(deviceId) == devicesToKeep.end();
438 });
439}
440
441void UnwantedInteractionBlocker::dump(std::string& dump) {
442 dump += "UnwantedInteractionBlocker:\n";
443 dump += StringPrintf(" mEnablePalmRejection: %s\n", toString(mEnablePalmRejection));
444 dump += StringPrintf(" isPalmRejectionEnabled (flag value): %s\n",
445 toString(isPalmRejectionEnabled()));
446 dump += mPalmRejectors.empty() ? " mPalmRejectors: None\n" : " mPalmRejectors:\n";
447 for (const auto& [deviceId, palmRejector] : mPalmRejectors) {
448 dump += StringPrintf(" deviceId = %" PRId32 ":\n", deviceId);
449 dump += addPrefix(palmRejector.dump(), " ");
450 }
451}
452
453void UnwantedInteractionBlocker::monitor() {}
454
455UnwantedInteractionBlocker::~UnwantedInteractionBlocker() {}
456
457void SlotState::update(const NotifyMotionArgs& args) {
458 for (size_t i = 0; i < args.pointerCount; i++) {
459 const int32_t pointerId = args.pointerProperties[i].id;
460 const int32_t resolvedAction = resolveActionForPointer(i, args.action);
461 processPointerId(pointerId, resolvedAction);
462 }
463}
464
465size_t SlotState::findUnusedSlot() const {
466 size_t unusedSlot = 0;
467 // Since the collection is ordered, we can rely on the in-order traversal
468 for (const auto& [slot, trackingId] : mPointerIdsBySlot) {
469 if (unusedSlot != slot) {
470 break;
471 }
472 unusedSlot++;
473 }
474 return unusedSlot;
475}
476
477void SlotState::processPointerId(int pointerId, int32_t actionMasked) {
478 switch (MotionEvent::getActionMasked(actionMasked)) {
479 case AMOTION_EVENT_ACTION_DOWN:
480 case AMOTION_EVENT_ACTION_POINTER_DOWN:
481 case AMOTION_EVENT_ACTION_HOVER_ENTER: {
482 // New pointer going down
483 size_t newSlot = findUnusedSlot();
484 mPointerIdsBySlot[newSlot] = pointerId;
485 mSlotsByPointerId[pointerId] = newSlot;
486 return;
487 }
488 case AMOTION_EVENT_ACTION_MOVE:
489 case AMOTION_EVENT_ACTION_HOVER_MOVE: {
490 return;
491 }
492 case AMOTION_EVENT_ACTION_CANCEL:
493 case AMOTION_EVENT_ACTION_POINTER_UP:
494 case AMOTION_EVENT_ACTION_UP:
495 case AMOTION_EVENT_ACTION_HOVER_EXIT: {
496 auto it = mSlotsByPointerId.find(pointerId);
497 LOG_ALWAYS_FATAL_IF(it == mSlotsByPointerId.end());
498 size_t slot = it->second;
499 // Erase this pointer from both collections
500 mPointerIdsBySlot.erase(slot);
501 mSlotsByPointerId.erase(pointerId);
502 return;
503 }
504 }
505 LOG_ALWAYS_FATAL("Unhandled action : %s", MotionEvent::actionToString(actionMasked).c_str());
506 return;
507}
508
509std::optional<size_t> SlotState::getSlotForPointerId(int32_t pointerId) const {
510 auto it = mSlotsByPointerId.find(pointerId);
511 if (it == mSlotsByPointerId.end()) {
512 return std::nullopt;
513 }
514 return it->second;
515}
516
517std::string SlotState::dump() const {
518 std::string out = "mSlotsByPointerId:\n";
519 out += addPrefix(dumpMap(mSlotsByPointerId), " ") + "\n";
520 out += "mPointerIdsBySlot:\n";
521 out += addPrefix(dumpMap(mPointerIdsBySlot), " ") + "\n";
522 return out;
523}
524
525PalmRejector::PalmRejector(const AndroidPalmFilterDeviceInfo& info,
526 std::unique_ptr<::ui::PalmDetectionFilter> filter)
527 : mSharedPalmState(std::make_unique<::ui::SharedPalmDetectionFilterState>()),
528 mDeviceInfo(info),
529 mPalmDetectionFilter(std::move(filter)) {
530 if (mPalmDetectionFilter != nullptr) {
531 // This path is used for testing. Non-testing invocations should let this constructor
532 // create a real PalmDetectionFilter
533 return;
534 }
535 std::unique_ptr<::ui::NeuralStylusPalmDetectionFilterModel> model =
536 std::make_unique<::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel>(
537 std::vector<float>());
538 mPalmDetectionFilter =
539 std::make_unique<::ui::NeuralStylusPalmDetectionFilter>(mDeviceInfo, std::move(model),
540 mSharedPalmState.get());
541}
542
543std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args,
544 const AndroidPalmFilterDeviceInfo& deviceInfo,
545 const SlotState& oldSlotState,
546 const SlotState& newSlotState) {
547 std::vector<::ui::InProgressTouchEvdev> touches;
548
549 for (size_t i = 0; i < args.pointerCount; i++) {
550 const int32_t pointerId = args.pointerProperties[i].id;
551 touches.emplace_back(::ui::InProgressTouchEvdev());
552 touches.back().major = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
553 touches.back().minor = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
554 touches.back().tool_type = getLinuxToolType(args.pointerProperties[i].toolType);
555
556 // Whether there is new information for the touch.
557 touches.back().altered = true;
558
559 // Whether the touch was cancelled. Touch events should be ignored till a
560 // new touch is initiated.
561 touches.back().was_cancelled = false;
562
563 // Whether the touch is going to be canceled.
564 touches.back().cancelled = false;
565
566 // Whether the touch is delayed at first appearance. Will not be reported yet.
567 touches.back().delayed = false;
568
569 // Whether the touch was delayed before.
570 touches.back().was_delayed = false;
571
572 // Whether the touch is held until end or no longer held.
573 touches.back().held = false;
574
575 // Whether this touch was held before being sent.
576 touches.back().was_held = false;
577
578 const int32_t resolvedAction = resolveActionForPointer(i, args.action);
579 const bool isDown = resolvedAction == AMOTION_EVENT_ACTION_POINTER_DOWN ||
580 resolvedAction == AMOTION_EVENT_ACTION_DOWN;
581 touches.back().was_touching = !isDown;
582
583 const bool isUpOrCancel = resolvedAction == AMOTION_EVENT_ACTION_CANCEL ||
584 resolvedAction == AMOTION_EVENT_ACTION_UP ||
585 resolvedAction == AMOTION_EVENT_ACTION_POINTER_UP;
586
587 touches.back().x = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X);
588 touches.back().y = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y);
589
590 std::optional<size_t> slot = newSlotState.getSlotForPointerId(pointerId);
591 if (!slot) {
592 slot = oldSlotState.getSlotForPointerId(pointerId);
593 }
594 LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer %d", pointerId);
595 touches.back().slot = *slot;
596 touches.back().tracking_id = (!isUpOrCancel) ? pointerId : -1;
597 touches.back().touching = !isUpOrCancel;
598
599 // The fields 'radius_x' and 'radius_x' are not used for palm rejection
600 touches.back().pressure = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
601 touches.back().tool_code = BTN_TOOL_FINGER;
602 // The field 'orientation' is not used for palm rejection
603 // The fields 'tilt_x' and 'tilt_y' are not used for palm rejection
604 touches.back().reported_tool_type = ::ui::EventPointerType::kTouch;
605 touches.back().stylus_button = false;
606 }
607 return touches;
608}
609
610std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs& args) {
611 if (mPalmDetectionFilter == nullptr) {
612 return {args};
613 }
614 const bool skipThisEvent = args.action == AMOTION_EVENT_ACTION_HOVER_ENTER ||
615 args.action == AMOTION_EVENT_ACTION_HOVER_MOVE ||
616 args.action == AMOTION_EVENT_ACTION_HOVER_EXIT ||
617 args.action == AMOTION_EVENT_ACTION_BUTTON_PRESS ||
618 args.action == AMOTION_EVENT_ACTION_BUTTON_RELEASE ||
619 args.action == AMOTION_EVENT_ACTION_SCROLL;
620 if (skipThisEvent) {
621 // Lets not process hover events, button events, or scroll for now.
622 return {args};
623 }
624 if (args.action == AMOTION_EVENT_ACTION_DOWN) {
625 mSuppressedPointerIds.clear();
626 }
627 std::bitset<::ui::kNumTouchEvdevSlots> slotsToHold;
628 std::bitset<::ui::kNumTouchEvdevSlots> slotsToSuppress;
629
630 // Store the slot state before we call getTouches and update it. This way, we can find
631 // the slots that have been removed due to the incoming event.
632 SlotState oldSlotState = mSlotState;
633 mSlotState.update(args);
634 std::vector<::ui::InProgressTouchEvdev> touches =
635 getTouches(args, mDeviceInfo, oldSlotState, mSlotState);
636 ::base::TimeTicks chromeTimestamp = toChromeTimestamp(args.eventTime);
637
638 mPalmDetectionFilter->Filter(touches, chromeTimestamp, &slotsToHold, &slotsToSuppress);
639
640 // Now that we know which slots should be suppressed, let's convert those to pointer id's.
641 std::set<int32_t> oldSuppressedIds;
642 std::swap(oldSuppressedIds, mSuppressedPointerIds);
643 for (size_t i = 0; i < args.pointerCount; i++) {
644 const int32_t pointerId = args.pointerProperties[i].id;
645 std::optional<size_t> slot = oldSlotState.getSlotForPointerId(pointerId);
646 if (!slot) {
647 slot = mSlotState.getSlotForPointerId(pointerId);
648 LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer id %" PRId32, pointerId);
649 }
650 if (slotsToSuppress.test(*slot)) {
651 mSuppressedPointerIds.insert(pointerId);
652 }
653 }
654
655 std::vector<NotifyMotionArgs> argsWithoutUnwantedPointers =
656 cancelSuppressedPointers(args, oldSuppressedIds, mSuppressedPointerIds);
657 for (const NotifyMotionArgs& checkArgs : argsWithoutUnwantedPointers) {
658 LOG_ALWAYS_FATAL_IF(checkArgs.action == ACTION_UNKNOWN, "%s", checkArgs.dump().c_str());
659 }
660
661 if (mSuppressedPointerIds != oldSuppressedIds) {
662 if (argsWithoutUnwantedPointers.size() != 1 ||
663 argsWithoutUnwantedPointers[0].pointerCount != args.pointerCount) {
664 ALOGI("Palm detected, removing pointer ids %s from %s",
665 dumpSet(mSuppressedPointerIds).c_str(), args.dump().c_str());
666 }
667 }
668
669 return argsWithoutUnwantedPointers;
670}
671
672const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() {
673 return mDeviceInfo;
674}
675
676std::string PalmRejector::dump() const {
677 std::string out;
678 out += "mDeviceInfo:\n";
679 out += addPrefix(dumpDeviceInfo(mDeviceInfo), " ");
680 out += "mSlotState:\n";
681 out += addPrefix(mSlotState.dump(), " ");
682 out += "mSuppressedPointerIds: ";
683 out += dumpSet(mSuppressedPointerIds) + "\n";
684 return out;
685}
686
687} // namespace android