blob: 6c4b11e0e5f44a5621c994af230c96203d438a25 [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
Siarhei Vishniakoua028c442019-02-04 14:33:23 -080032#define INDENT1 " "
33#define INDENT2 " "
34#define INDENT3 " "
35#define INDENT4 " "
36#define INDENT5 " "
37
38using android::base::StringPrintf;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080039using android::hardware::hidl_bitfield;
40using android::hardware::hidl_vec;
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -080041using android::hardware::Return;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080042using namespace android::hardware::input;
43
44namespace android {
45
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080046//Max number of elements to store in mEvents.
47static constexpr size_t MAX_EVENTS = 5;
48
49template<class K, class V>
50static V getValueForKey(const std::unordered_map<K, V>& map, K key, V defaultValue) {
51 auto it = map.find(key);
52 if (it == map.end()) {
53 return defaultValue;
54 }
55 return it->second;
56}
57
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080058static MotionClassification getMotionClassification(common::V1_0::Classification classification) {
59 static_assert(MotionClassification::NONE ==
60 static_cast<MotionClassification>(common::V1_0::Classification::NONE));
61 static_assert(MotionClassification::AMBIGUOUS_GESTURE ==
62 static_cast<MotionClassification>(common::V1_0::Classification::AMBIGUOUS_GESTURE));
63 static_assert(MotionClassification::DEEP_PRESS ==
64 static_cast<MotionClassification>(common::V1_0::Classification::DEEP_PRESS));
65 return static_cast<MotionClassification>(classification);
66}
67
68static bool isTouchEvent(const NotifyMotionArgs& args) {
Siarhei Vishniakoud9489572021-11-12 20:08:38 -080069 return isFromSource(args.source, AINPUT_SOURCE_TOUCHPAD) ||
70 isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080071}
72
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080073// --- ClassifierEvent ---
74
75ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
76 type(ClassifierEventType::MOTION), args(std::move(args)) { };
77ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args) :
78 type(ClassifierEventType::DEVICE_RESET), args(std::move(args)) { };
79ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args) :
80 type(type), args(std::move(args)) { };
81
82ClassifierEvent::ClassifierEvent(ClassifierEvent&& other) :
83 type(other.type), args(std::move(other.args)) { };
84
85ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) {
86 type = other.type;
87 args = std::move(other.args);
88 return *this;
89}
90
91ClassifierEvent ClassifierEvent::createHalResetEvent() {
92 return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr);
93}
94
95ClassifierEvent ClassifierEvent::createExitEvent() {
96 return ClassifierEvent(ClassifierEventType::EXIT, nullptr);
97}
98
99std::optional<int32_t> ClassifierEvent::getDeviceId() const {
100 switch (type) {
101 case ClassifierEventType::MOTION: {
102 NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(args.get());
103 return motionArgs->deviceId;
104 }
105 case ClassifierEventType::DEVICE_RESET: {
106 NotifyDeviceResetArgs* deviceResetArgs =
107 static_cast<NotifyDeviceResetArgs*>(args.get());
108 return deviceResetArgs->deviceId;
109 }
110 case ClassifierEventType::HAL_RESET: {
111 return std::nullopt;
112 }
113 case ClassifierEventType::EXIT: {
114 return std::nullopt;
115 }
116 }
117}
118
119// --- MotionClassifier ---
120
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800121MotionClassifier::MotionClassifier(
122 sp<android::hardware::input::classifier::V1_0::IInputClassifier> service)
123 : mEvents(MAX_EVENTS), mService(service) {
Siarhei Vishniakou6dbc3f62019-02-04 14:30:11 -0800124 // Under normal operation, we do not need to reset the HAL here. But in the case where system
125 // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
126 // have received events in the past. That means, that HAL could be in an inconsistent state
127 // once it receives events from the newly created MotionClassifier.
128 mEvents.push(ClassifierEvent::createHalResetEvent());
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700129
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800130 mHalThread = std::thread(&MotionClassifier::processEvents, this);
131#if defined(__linux__)
132 // Set the thread name for debugging
133 pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
134#endif
135}
136
137std::unique_ptr<MotionClassifierInterface> MotionClassifier::create(
138 sp<android::hardware::hidl_death_recipient> deathRecipient) {
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800139 sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
140 classifier::V1_0::IInputClassifier::getService();
141 if (!service) {
142 // Not really an error, maybe the device does not have this HAL,
143 // but somehow the feature flag is flipped
144 ALOGI("Could not obtain InputClassifier HAL");
145 return nullptr;
146 }
147
148 const bool linked = service->linkToDeath(deathRecipient, 0 /* cookie */).withDefault(false);
149 if (!linked) {
150 ALOGE("Could not link death recipient to the HAL death");
151 return nullptr;
152 }
153 // Using 'new' to access a non-public constructor
154 return std::unique_ptr<MotionClassifier>(new MotionClassifier(service));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800155}
156
157MotionClassifier::~MotionClassifier() {
158 requestExit();
159 mHalThread.join();
160}
161
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800162/**
163 * Obtain the classification from the HAL for a given MotionEvent.
164 * Should only be called from the InputClassifier thread (mHalThread).
165 * Should not be called from the thread that notifyMotion runs on.
166 *
167 * There is no way to provide a timeout for a HAL call. So if the HAL takes too long
168 * to return a classification, this would directly impact the touch latency.
169 * To remove any possibility of negatively affecting the touch latency, the HAL
170 * is called from a dedicated thread.
171 */
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800172void MotionClassifier::processEvents() {
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800173 while (true) {
174 ClassifierEvent event = mEvents.pop();
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800175 bool halResponseOk = true;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800176 switch (event.type) {
177 case ClassifierEventType::MOTION: {
178 NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
Siarhei Vishniakoua47a4d42019-05-06 17:14:11 -0700179 common::V1_0::MotionEvent motionEvent =
180 notifyMotionArgsToHalMotionEvent(*motionArgs);
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800181 Return<common::V1_0::Classification> response = mService->classify(motionEvent);
182 halResponseOk = response.isOk();
183 if (halResponseOk) {
184 common::V1_0::Classification halClassification = response;
185 updateClassification(motionArgs->deviceId, motionArgs->eventTime,
186 getMotionClassification(halClassification));
187 }
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800188 break;
189 }
190 case ClassifierEventType::DEVICE_RESET: {
191 const int32_t deviceId = *(event.getDeviceId());
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800192 halResponseOk = mService->resetDevice(deviceId).isOk();
Siarhei Vishniakoue3021d72020-02-28 15:25:41 -0800193 clearDeviceState(deviceId);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800194 break;
195 }
196 case ClassifierEventType::HAL_RESET: {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800197 halResponseOk = mService->reset().isOk();
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800198 clearClassifications();
199 break;
200 }
201 case ClassifierEventType::EXIT: {
202 clearClassifications();
203 return;
204 }
205 }
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800206 if (!halResponseOk) {
207 ALOGE("Error communicating with InputClassifier HAL. "
208 "Exiting MotionClassifier HAL thread");
209 clearClassifications();
210 return;
211 }
212 }
213}
214
215void MotionClassifier::enqueueEvent(ClassifierEvent&& event) {
216 bool eventAdded = mEvents.push(std::move(event));
217 if (!eventAdded) {
218 // If the queue is full, suspect the HAL is slow in processing the events.
Siarhei Vishniakou9b5a8212019-07-01 14:25:40 -0700219 ALOGE("Could not add the event to the queue. Resetting");
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800220 reset();
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800221 }
222}
223
224void MotionClassifier::requestExit() {
225 reset();
226 mEvents.push(ClassifierEvent::createExitEvent());
227}
228
229void MotionClassifier::updateClassification(int32_t deviceId, nsecs_t eventTime,
230 MotionClassification classification) {
231 std::scoped_lock lock(mLock);
232 const nsecs_t lastDownTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
233 if (eventTime < lastDownTime) {
234 // HAL just finished processing an event that belonged to an earlier gesture,
235 // but new gesture is already in progress. Drop this classification.
236 ALOGW("Received late classification. Late by at least %" PRId64 " ms.",
237 nanoseconds_to_milliseconds(lastDownTime - eventTime));
238 return;
239 }
240 mClassifications[deviceId] = classification;
241}
242
243void MotionClassifier::setClassification(int32_t deviceId, MotionClassification classification) {
244 std::scoped_lock lock(mLock);
245 mClassifications[deviceId] = classification;
246}
247
248void MotionClassifier::clearClassifications() {
249 std::scoped_lock lock(mLock);
250 mClassifications.clear();
251}
252
253MotionClassification MotionClassifier::getClassification(int32_t deviceId) {
254 std::scoped_lock lock(mLock);
255 return getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
256}
257
258void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) {
259 std::scoped_lock lock(mLock);
260 mLastDownTimes[deviceId] = downTime;
261 mClassifications[deviceId] = MotionClassification::NONE;
262}
263
Siarhei Vishniakoue3021d72020-02-28 15:25:41 -0800264void MotionClassifier::clearDeviceState(int32_t deviceId) {
265 std::scoped_lock lock(mLock);
266 mClassifications.erase(deviceId);
267 mLastDownTimes.erase(deviceId);
268}
269
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800270MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800271 if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
272 updateLastDownTime(args.deviceId, args.downTime);
273 }
274
275 ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args));
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800276 enqueueEvent(std::move(event));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800277 return getClassification(args.deviceId);
278}
279
280void MotionClassifier::reset() {
281 mEvents.clear();
282 mEvents.push(ClassifierEvent::createHalResetEvent());
283}
284
285/**
286 * Per-device reset. Clear the outstanding events that are going to be sent to HAL.
287 * Request InputClassifier thread to call resetDevice for this particular device.
288 */
289void MotionClassifier::reset(const NotifyDeviceResetArgs& args) {
290 int32_t deviceId = args.deviceId;
291 // Clear the pending events right away, to avoid unnecessary work done by the HAL.
292 mEvents.erase([deviceId](const ClassifierEvent& event) {
293 std::optional<int32_t> eventDeviceId = event.getDeviceId();
294 return eventDeviceId && (*eventDeviceId == deviceId);
295 });
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800296 enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800297}
298
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700299const char* MotionClassifier::getServiceStatus() REQUIRES(mLock) {
300 if (!mService) {
301 return "null";
302 }
303 if (mService->ping().isOk()) {
304 return "running";
305 }
306 return "not responding";
307}
308
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800309void MotionClassifier::dump(std::string& dump) {
310 std::scoped_lock lock(mLock);
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700311 dump += StringPrintf(INDENT2 "mService status: %s\n", getServiceStatus());
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800312 dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n",
313 mEvents.size(), MAX_EVENTS);
314 dump += INDENT2 "mClassifications, mLastDownTimes:\n";
315 dump += INDENT3 "Device Id\tClassification\tLast down time";
316 // Combine mClassifications and mLastDownTimes into a single table.
317 // Create a superset of device ids.
318 std::unordered_set<int32_t> deviceIds;
319 std::for_each(mClassifications.begin(), mClassifications.end(),
320 [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
321 std::for_each(mLastDownTimes.begin(), mLastDownTimes.end(),
322 [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
323 for(int32_t deviceId : deviceIds) {
324 const MotionClassification classification =
325 getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
326 const nsecs_t downTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
327 dump += StringPrintf("\n" INDENT4 "%" PRId32 "\t%s\t%" PRId64,
328 deviceId, motionClassificationToString(classification), downTime);
329 }
330}
331
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800332// --- HalDeathRecipient
333
334InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {}
335
336void InputClassifier::HalDeathRecipient::serviceDied(
337 uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) {
338 sp<android::hidl::base::V1_0::IBase> service = who.promote();
339 if (service) {
340 service->unlinkToDeath(this);
341 }
342 mParent.setMotionClassifier(nullptr);
343}
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800344
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800345// --- InputClassifier ---
346
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700347InputClassifier::InputClassifier(InputListenerInterface& listener)
Siarhei Vishniakouc9ac19e2020-03-19 11:55:01 -0700348 : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
349
350void InputClassifier::setMotionClassifierEnabled(bool enabled) {
351 if (enabled) {
352 ALOGI("Enabling motion classifier");
353 if (mInitializeMotionClassifierThread.joinable()) {
354 mInitializeMotionClassifierThread.join();
355 }
356 mInitializeMotionClassifierThread = std::thread(
357 [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); });
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800358#if defined(__linux__)
Siarhei Vishniakouc9ac19e2020-03-19 11:55:01 -0700359 // Set the thread name for debugging
360 pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
361 "Create MotionClassifier");
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800362#endif
Siarhei Vishniakouc9ac19e2020-03-19 11:55:01 -0700363 } else {
364 ALOGI("Disabling motion classifier");
365 setMotionClassifier(nullptr);
366 }
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800367}
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800368
369void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
370 // pass through
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700371 mListener.notifyConfigurationChanged(args);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800372}
373
374void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
375 // pass through
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700376 mListener.notifyKey(args);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800377}
378
379void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800380 std::scoped_lock lock(mLock);
381 // MotionClassifier is only used for touch events, for now
382 const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
383 if (!sendToMotionClassifier) {
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700384 mListener.notifyMotion(args);
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800385 return;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800386 }
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800387
388 NotifyMotionArgs newArgs(*args);
389 newArgs.classification = mMotionClassifier->classify(newArgs);
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700390 mListener.notifyMotion(&newArgs);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800391}
392
Chris Yef59a2f42020-10-16 12:55:26 -0700393void InputClassifier::notifySensor(const NotifySensorArgs* args) {
394 // pass through
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700395 mListener.notifySensor(args);
Chris Yef59a2f42020-10-16 12:55:26 -0700396}
397
Chris Yefb552902021-02-03 17:18:37 -0800398void InputClassifier::notifyVibratorState(const NotifyVibratorStateArgs* args) {
399 // pass through
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700400 mListener.notifyVibratorState(args);
Chris Yefb552902021-02-03 17:18:37 -0800401}
402
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800403void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
404 // pass through
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700405 mListener.notifySwitch(args);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800406}
407
408void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800409 std::scoped_lock lock(mLock);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800410 if (mMotionClassifier) {
411 mMotionClassifier->reset(*args);
412 }
413 // continue to next stage
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700414 mListener.notifyDeviceReset(args);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800415}
416
Prabir Pradhan7e186182020-11-10 13:56:45 -0800417void InputClassifier::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
418 // pass through
Siarhei Vishniakou18050092021-09-01 13:32:49 -0700419 mListener.notifyPointerCaptureChanged(args);
Prabir Pradhan7e186182020-11-10 13:56:45 -0800420}
421
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800422void InputClassifier::setMotionClassifier(
423 std::unique_ptr<MotionClassifierInterface> motionClassifier) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800424 std::scoped_lock lock(mLock);
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800425 mMotionClassifier = std::move(motionClassifier);
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800426}
427
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800428void InputClassifier::dump(std::string& dump) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800429 std::scoped_lock lock(mLock);
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800430 dump += "Input Classifier State:\n";
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800431 dump += INDENT1 "Motion Classifier:\n";
432 if (mMotionClassifier) {
433 mMotionClassifier->dump(dump);
434 } else {
435 dump += INDENT2 "<nullptr>";
436 }
437 dump += "\n";
438}
439
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800440InputClassifier::~InputClassifier() {
Siarhei Vishniakouc9ac19e2020-03-19 11:55:01 -0700441 if (mInitializeMotionClassifierThread.joinable()) {
442 mInitializeMotionClassifierThread.join();
443 }
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800444}
445
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700446} // namespace android