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