blob: 19cad7b9ad702e5a462a83dfe20d2281895c7db8 [file] [log] [blame]
Siarhei Vishniakou473174e2017-12-27 16:44:42 -08001/*
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#define LOG_TAG "InputClassifier"
18
19#include "InputClassifier.h"
Siarhei Vishniakoua47a4d42019-05-06 17:14:11 -070020#include "InputClassifierConverter.h"
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080021
22#include <algorithm>
Siarhei Vishniakoua028c442019-02-04 14:33:23 -080023#include <android-base/stringprintf.h>
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080024#include <cmath>
25#include <inttypes.h>
26#include <log/log.h>
27#if defined(__linux__)
28 #include <pthread.h>
29#endif
Siarhei Vishniakoua028c442019-02-04 14:33:23 -080030#include <unordered_set>
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080031
32#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
33
Siarhei Vishniakoua028c442019-02-04 14:33:23 -080034#define INDENT1 " "
35#define INDENT2 " "
36#define INDENT3 " "
37#define INDENT4 " "
38#define INDENT5 " "
39
40using android::base::StringPrintf;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080041using android::hardware::hidl_bitfield;
42using android::hardware::hidl_vec;
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -080043using android::hardware::Return;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080044using namespace android::hardware::input;
45
46namespace android {
47
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080048//Max number of elements to store in mEvents.
49static constexpr size_t MAX_EVENTS = 5;
50
51template<class K, class V>
52static V getValueForKey(const std::unordered_map<K, V>& map, K key, V defaultValue) {
53 auto it = map.find(key);
54 if (it == map.end()) {
55 return defaultValue;
56 }
57 return it->second;
58}
59
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080060static MotionClassification getMotionClassification(common::V1_0::Classification classification) {
61 static_assert(MotionClassification::NONE ==
62 static_cast<MotionClassification>(common::V1_0::Classification::NONE));
63 static_assert(MotionClassification::AMBIGUOUS_GESTURE ==
64 static_cast<MotionClassification>(common::V1_0::Classification::AMBIGUOUS_GESTURE));
65 static_assert(MotionClassification::DEEP_PRESS ==
66 static_cast<MotionClassification>(common::V1_0::Classification::DEEP_PRESS));
67 return static_cast<MotionClassification>(classification);
68}
69
70static bool isTouchEvent(const NotifyMotionArgs& args) {
Siarhei Vishniakoud9489572021-11-12 20:08:38 -080071 return isFromSource(args.source, AINPUT_SOURCE_TOUCHPAD) ||
72 isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080073}
74
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080075// --- ClassifierEvent ---
76
77ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
78 type(ClassifierEventType::MOTION), args(std::move(args)) { };
79ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args) :
80 type(ClassifierEventType::DEVICE_RESET), args(std::move(args)) { };
81ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args) :
82 type(type), args(std::move(args)) { };
83
84ClassifierEvent::ClassifierEvent(ClassifierEvent&& other) :
85 type(other.type), args(std::move(other.args)) { };
86
87ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) {
88 type = other.type;
89 args = std::move(other.args);
90 return *this;
91}
92
93ClassifierEvent ClassifierEvent::createHalResetEvent() {
94 return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr);
95}
96
97ClassifierEvent ClassifierEvent::createExitEvent() {
98 return ClassifierEvent(ClassifierEventType::EXIT, nullptr);
99}
100
101std::optional<int32_t> ClassifierEvent::getDeviceId() const {
102 switch (type) {
103 case ClassifierEventType::MOTION: {
104 NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(args.get());
105 return motionArgs->deviceId;
106 }
107 case ClassifierEventType::DEVICE_RESET: {
108 NotifyDeviceResetArgs* deviceResetArgs =
109 static_cast<NotifyDeviceResetArgs*>(args.get());
110 return deviceResetArgs->deviceId;
111 }
112 case ClassifierEventType::HAL_RESET: {
113 return std::nullopt;
114 }
115 case ClassifierEventType::EXIT: {
116 return std::nullopt;
117 }
118 }
119}
120
121// --- MotionClassifier ---
122
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800123MotionClassifier::MotionClassifier(
124 sp<android::hardware::input::classifier::V1_0::IInputClassifier> service)
125 : mEvents(MAX_EVENTS), mService(service) {
Siarhei Vishniakou6dbc3f62019-02-04 14:30:11 -0800126 // Under normal operation, we do not need to reset the HAL here. But in the case where system
127 // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
128 // have received events in the past. That means, that HAL could be in an inconsistent state
129 // once it receives events from the newly created MotionClassifier.
130 mEvents.push(ClassifierEvent::createHalResetEvent());
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700131
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800132 mHalThread = std::thread(&MotionClassifier::processEvents, this);
133#if defined(__linux__)
134 // Set the thread name for debugging
135 pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
136#endif
137}
138
139std::unique_ptr<MotionClassifierInterface> MotionClassifier::create(
140 sp<android::hardware::hidl_death_recipient> deathRecipient) {
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800141 sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
142 classifier::V1_0::IInputClassifier::getService();
143 if (!service) {
144 // Not really an error, maybe the device does not have this HAL,
145 // but somehow the feature flag is flipped
146 ALOGI("Could not obtain InputClassifier HAL");
147 return nullptr;
148 }
149
150 const bool linked = service->linkToDeath(deathRecipient, 0 /* cookie */).withDefault(false);
151 if (!linked) {
152 ALOGE("Could not link death recipient to the HAL death");
153 return nullptr;
154 }
155 // Using 'new' to access a non-public constructor
156 return std::unique_ptr<MotionClassifier>(new MotionClassifier(service));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800157}
158
159MotionClassifier::~MotionClassifier() {
160 requestExit();
161 mHalThread.join();
162}
163
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800164/**
165 * Obtain the classification from the HAL for a given MotionEvent.
166 * Should only be called from the InputClassifier thread (mHalThread).
167 * Should not be called from the thread that notifyMotion runs on.
168 *
169 * There is no way to provide a timeout for a HAL call. So if the HAL takes too long
170 * to return a classification, this would directly impact the touch latency.
171 * To remove any possibility of negatively affecting the touch latency, the HAL
172 * is called from a dedicated thread.
173 */
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800174void MotionClassifier::processEvents() {
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800175 while (true) {
176 ClassifierEvent event = mEvents.pop();
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800177 bool halResponseOk = true;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800178 switch (event.type) {
179 case ClassifierEventType::MOTION: {
180 NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
Siarhei Vishniakoua47a4d42019-05-06 17:14:11 -0700181 common::V1_0::MotionEvent motionEvent =
182 notifyMotionArgsToHalMotionEvent(*motionArgs);
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800183 Return<common::V1_0::Classification> response = mService->classify(motionEvent);
184 halResponseOk = response.isOk();
185 if (halResponseOk) {
186 common::V1_0::Classification halClassification = response;
187 updateClassification(motionArgs->deviceId, motionArgs->eventTime,
188 getMotionClassification(halClassification));
189 }
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800190 break;
191 }
192 case ClassifierEventType::DEVICE_RESET: {
193 const int32_t deviceId = *(event.getDeviceId());
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800194 halResponseOk = mService->resetDevice(deviceId).isOk();
Siarhei Vishniakoue3021d72020-02-28 15:25:41 -0800195 clearDeviceState(deviceId);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800196 break;
197 }
198 case ClassifierEventType::HAL_RESET: {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800199 halResponseOk = mService->reset().isOk();
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800200 clearClassifications();
201 break;
202 }
203 case ClassifierEventType::EXIT: {
204 clearClassifications();
205 return;
206 }
207 }
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800208 if (!halResponseOk) {
209 ALOGE("Error communicating with InputClassifier HAL. "
210 "Exiting MotionClassifier HAL thread");
211 clearClassifications();
212 return;
213 }
214 }
215}
216
217void MotionClassifier::enqueueEvent(ClassifierEvent&& event) {
218 bool eventAdded = mEvents.push(std::move(event));
219 if (!eventAdded) {
220 // If the queue is full, suspect the HAL is slow in processing the events.
Siarhei Vishniakou9b5a8212019-07-01 14:25:40 -0700221 ALOGE("Could not add the event to the queue. Resetting");
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800222 reset();
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800223 }
224}
225
226void MotionClassifier::requestExit() {
227 reset();
228 mEvents.push(ClassifierEvent::createExitEvent());
229}
230
231void MotionClassifier::updateClassification(int32_t deviceId, nsecs_t eventTime,
232 MotionClassification classification) {
233 std::scoped_lock lock(mLock);
234 const nsecs_t lastDownTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
235 if (eventTime < lastDownTime) {
236 // HAL just finished processing an event that belonged to an earlier gesture,
237 // but new gesture is already in progress. Drop this classification.
238 ALOGW("Received late classification. Late by at least %" PRId64 " ms.",
239 nanoseconds_to_milliseconds(lastDownTime - eventTime));
240 return;
241 }
242 mClassifications[deviceId] = classification;
243}
244
245void MotionClassifier::setClassification(int32_t deviceId, MotionClassification classification) {
246 std::scoped_lock lock(mLock);
247 mClassifications[deviceId] = classification;
248}
249
250void MotionClassifier::clearClassifications() {
251 std::scoped_lock lock(mLock);
252 mClassifications.clear();
253}
254
255MotionClassification MotionClassifier::getClassification(int32_t deviceId) {
256 std::scoped_lock lock(mLock);
257 return getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
258}
259
260void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) {
261 std::scoped_lock lock(mLock);
262 mLastDownTimes[deviceId] = downTime;
263 mClassifications[deviceId] = MotionClassification::NONE;
264}
265
Siarhei Vishniakoue3021d72020-02-28 15:25:41 -0800266void MotionClassifier::clearDeviceState(int32_t deviceId) {
267 std::scoped_lock lock(mLock);
268 mClassifications.erase(deviceId);
269 mLastDownTimes.erase(deviceId);
270}
271
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800272MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800273 if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
274 updateLastDownTime(args.deviceId, args.downTime);
275 }
276
277 ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args));
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800278 enqueueEvent(std::move(event));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800279 return getClassification(args.deviceId);
280}
281
282void MotionClassifier::reset() {
283 mEvents.clear();
284 mEvents.push(ClassifierEvent::createHalResetEvent());
285}
286
287/**
288 * Per-device reset. Clear the outstanding events that are going to be sent to HAL.
289 * Request InputClassifier thread to call resetDevice for this particular device.
290 */
291void MotionClassifier::reset(const NotifyDeviceResetArgs& args) {
292 int32_t deviceId = args.deviceId;
293 // Clear the pending events right away, to avoid unnecessary work done by the HAL.
294 mEvents.erase([deviceId](const ClassifierEvent& event) {
295 std::optional<int32_t> eventDeviceId = event.getDeviceId();
296 return eventDeviceId && (*eventDeviceId == deviceId);
297 });
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800298 enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800299}
300
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700301const char* MotionClassifier::getServiceStatus() REQUIRES(mLock) {
302 if (!mService) {
303 return "null";
304 }
305 if (mService->ping().isOk()) {
306 return "running";
307 }
308 return "not responding";
309}
310
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800311void MotionClassifier::dump(std::string& dump) {
312 std::scoped_lock lock(mLock);
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700313 dump += StringPrintf(INDENT2 "mService status: %s\n", getServiceStatus());
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800314 dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n",
315 mEvents.size(), MAX_EVENTS);
316 dump += INDENT2 "mClassifications, mLastDownTimes:\n";
317 dump += INDENT3 "Device Id\tClassification\tLast down time";
318 // Combine mClassifications and mLastDownTimes into a single table.
319 // Create a superset of device ids.
320 std::unordered_set<int32_t> deviceIds;
321 std::for_each(mClassifications.begin(), mClassifications.end(),
322 [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
323 std::for_each(mLastDownTimes.begin(), mLastDownTimes.end(),
324 [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
325 for(int32_t deviceId : deviceIds) {
326 const MotionClassification classification =
327 getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
328 const nsecs_t downTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
329 dump += StringPrintf("\n" INDENT4 "%" PRId32 "\t%s\t%" PRId64,
330 deviceId, motionClassificationToString(classification), downTime);
331 }
332}
333
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800334// --- HalDeathRecipient
335
336InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {}
337
338void InputClassifier::HalDeathRecipient::serviceDied(
339 uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) {
340 sp<android::hidl::base::V1_0::IBase> service = who.promote();
341 if (service) {
342 service->unlinkToDeath(this);
343 }
344 mParent.setMotionClassifier(nullptr);
345}
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800346
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800347// --- InputClassifier ---
348
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700349InputClassifier::InputClassifier(InputListenerInterface& listener)
Siarhei Vishniakouc9ac19e2020-03-19 11:55:01 -0700350 : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
351
352void InputClassifier::setMotionClassifierEnabled(bool enabled) {
353 if (enabled) {
354 ALOGI("Enabling motion classifier");
355 if (mInitializeMotionClassifierThread.joinable()) {
356 mInitializeMotionClassifierThread.join();
357 }
358 mInitializeMotionClassifierThread = std::thread(
359 [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); });
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800360#if defined(__linux__)
Siarhei Vishniakouc9ac19e2020-03-19 11:55:01 -0700361 // Set the thread name for debugging
362 pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
363 "Create MotionClassifier");
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800364#endif
Siarhei Vishniakouc9ac19e2020-03-19 11:55:01 -0700365 } else {
366 ALOGI("Disabling motion classifier");
367 setMotionClassifier(nullptr);
368 }
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800369}
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800370
371void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
372 // pass through
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700373 mListener.notifyConfigurationChanged(args);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800374}
375
376void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
377 // pass through
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700378 mListener.notifyKey(args);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800379}
380
381void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800382 std::scoped_lock lock(mLock);
383 // MotionClassifier is only used for touch events, for now
384 const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
385 if (!sendToMotionClassifier) {
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700386 mListener.notifyMotion(args);
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800387 return;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800388 }
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800389
390 NotifyMotionArgs newArgs(*args);
391 newArgs.classification = mMotionClassifier->classify(newArgs);
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700392 mListener.notifyMotion(&newArgs);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800393}
394
Chris Yef59a2f42020-10-16 12:55:26 -0700395void InputClassifier::notifySensor(const NotifySensorArgs* args) {
396 // pass through
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700397 mListener.notifySensor(args);
Chris Yef59a2f42020-10-16 12:55:26 -0700398}
399
Chris Yefb552902021-02-03 17:18:37 -0800400void InputClassifier::notifyVibratorState(const NotifyVibratorStateArgs* args) {
401 // pass through
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700402 mListener.notifyVibratorState(args);
Chris Yefb552902021-02-03 17:18:37 -0800403}
404
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800405void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
406 // pass through
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700407 mListener.notifySwitch(args);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800408}
409
410void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800411 std::scoped_lock lock(mLock);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800412 if (mMotionClassifier) {
413 mMotionClassifier->reset(*args);
414 }
415 // continue to next stage
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700416 mListener.notifyDeviceReset(args);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800417}
418
Prabir Pradhan7e186182020-11-10 13:56:45 -0800419void InputClassifier::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
420 // pass through
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700421 mListener.notifyPointerCaptureChanged(args);
Prabir Pradhan7e186182020-11-10 13:56:45 -0800422}
423
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800424void InputClassifier::setMotionClassifier(
425 std::unique_ptr<MotionClassifierInterface> motionClassifier) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800426 std::scoped_lock lock(mLock);
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800427 mMotionClassifier = std::move(motionClassifier);
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800428}
429
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800430void InputClassifier::dump(std::string& dump) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800431 std::scoped_lock lock(mLock);
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800432 dump += "Input Classifier State:\n";
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800433 dump += INDENT1 "Motion Classifier:\n";
434 if (mMotionClassifier) {
435 mMotionClassifier->dump(dump);
436 } else {
437 dump += INDENT2 "<nullptr>";
438 }
439 dump += "\n";
440}
441
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800442InputClassifier::~InputClassifier() {
Siarhei Vishniakouc9ac19e2020-03-19 11:55:01 -0700443 if (mInitializeMotionClassifierThread.joinable()) {
444 mInitializeMotionClassifierThread.join();
445 }
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800446}
447
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700448} // namespace android