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