blob: 77a07162694542699ce33dcb0369171c3f7f5169 [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) {
71 return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
72}
73
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080074// --- ClassifierEvent ---
75
76ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
77 type(ClassifierEventType::MOTION), args(std::move(args)) { };
78ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args) :
79 type(ClassifierEventType::DEVICE_RESET), args(std::move(args)) { };
80ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args) :
81 type(type), args(std::move(args)) { };
82
83ClassifierEvent::ClassifierEvent(ClassifierEvent&& other) :
84 type(other.type), args(std::move(other.args)) { };
85
86ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) {
87 type = other.type;
88 args = std::move(other.args);
89 return *this;
90}
91
92ClassifierEvent ClassifierEvent::createHalResetEvent() {
93 return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr);
94}
95
96ClassifierEvent ClassifierEvent::createExitEvent() {
97 return ClassifierEvent(ClassifierEventType::EXIT, nullptr);
98}
99
100std::optional<int32_t> ClassifierEvent::getDeviceId() const {
101 switch (type) {
102 case ClassifierEventType::MOTION: {
103 NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(args.get());
104 return motionArgs->deviceId;
105 }
106 case ClassifierEventType::DEVICE_RESET: {
107 NotifyDeviceResetArgs* deviceResetArgs =
108 static_cast<NotifyDeviceResetArgs*>(args.get());
109 return deviceResetArgs->deviceId;
110 }
111 case ClassifierEventType::HAL_RESET: {
112 return std::nullopt;
113 }
114 case ClassifierEventType::EXIT: {
115 return std::nullopt;
116 }
117 }
118}
119
120// --- MotionClassifier ---
121
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800122MotionClassifier::MotionClassifier(
123 sp<android::hardware::input::classifier::V1_0::IInputClassifier> service)
124 : mEvents(MAX_EVENTS), mService(service) {
Siarhei Vishniakou6dbc3f62019-02-04 14:30:11 -0800125 // Under normal operation, we do not need to reset the HAL here. But in the case where system
126 // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
127 // have received events in the past. That means, that HAL could be in an inconsistent state
128 // once it receives events from the newly created MotionClassifier.
129 mEvents.push(ClassifierEvent::createHalResetEvent());
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700130
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800131 mHalThread = std::thread(&MotionClassifier::processEvents, this);
132#if defined(__linux__)
133 // Set the thread name for debugging
134 pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
135#endif
136}
137
138std::unique_ptr<MotionClassifierInterface> MotionClassifier::create(
139 sp<android::hardware::hidl_death_recipient> deathRecipient) {
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800140 sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
141 classifier::V1_0::IInputClassifier::getService();
142 if (!service) {
143 // Not really an error, maybe the device does not have this HAL,
144 // but somehow the feature flag is flipped
145 ALOGI("Could not obtain InputClassifier HAL");
146 return nullptr;
147 }
148
149 const bool linked = service->linkToDeath(deathRecipient, 0 /* cookie */).withDefault(false);
150 if (!linked) {
151 ALOGE("Could not link death recipient to the HAL death");
152 return nullptr;
153 }
154 // Using 'new' to access a non-public constructor
155 return std::unique_ptr<MotionClassifier>(new MotionClassifier(service));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800156}
157
158MotionClassifier::~MotionClassifier() {
159 requestExit();
160 mHalThread.join();
161}
162
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800163/**
164 * Obtain the classification from the HAL for a given MotionEvent.
165 * Should only be called from the InputClassifier thread (mHalThread).
166 * Should not be called from the thread that notifyMotion runs on.
167 *
168 * There is no way to provide a timeout for a HAL call. So if the HAL takes too long
169 * to return a classification, this would directly impact the touch latency.
170 * To remove any possibility of negatively affecting the touch latency, the HAL
171 * is called from a dedicated thread.
172 */
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800173void MotionClassifier::processEvents() {
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800174 while (true) {
175 ClassifierEvent event = mEvents.pop();
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800176 bool halResponseOk = true;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800177 switch (event.type) {
178 case ClassifierEventType::MOTION: {
179 NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
Siarhei Vishniakoua47a4d42019-05-06 17:14:11 -0700180 common::V1_0::MotionEvent motionEvent =
181 notifyMotionArgsToHalMotionEvent(*motionArgs);
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800182 Return<common::V1_0::Classification> response = mService->classify(motionEvent);
183 halResponseOk = response.isOk();
184 if (halResponseOk) {
185 common::V1_0::Classification halClassification = response;
186 updateClassification(motionArgs->deviceId, motionArgs->eventTime,
187 getMotionClassification(halClassification));
188 }
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800189 break;
190 }
191 case ClassifierEventType::DEVICE_RESET: {
192 const int32_t deviceId = *(event.getDeviceId());
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800193 halResponseOk = mService->resetDevice(deviceId).isOk();
Siarhei Vishniakoue3021d72020-02-28 15:25:41 -0800194 clearDeviceState(deviceId);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800195 break;
196 }
197 case ClassifierEventType::HAL_RESET: {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800198 halResponseOk = mService->reset().isOk();
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800199 clearClassifications();
200 break;
201 }
202 case ClassifierEventType::EXIT: {
203 clearClassifications();
204 return;
205 }
206 }
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800207 if (!halResponseOk) {
208 ALOGE("Error communicating with InputClassifier HAL. "
209 "Exiting MotionClassifier HAL thread");
210 clearClassifications();
211 return;
212 }
213 }
214}
215
216void MotionClassifier::enqueueEvent(ClassifierEvent&& event) {
217 bool eventAdded = mEvents.push(std::move(event));
218 if (!eventAdded) {
219 // If the queue is full, suspect the HAL is slow in processing the events.
Siarhei Vishniakou9b5a8212019-07-01 14:25:40 -0700220 ALOGE("Could not add the event to the queue. Resetting");
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800221 reset();
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800222 }
223}
224
225void MotionClassifier::requestExit() {
226 reset();
227 mEvents.push(ClassifierEvent::createExitEvent());
228}
229
230void MotionClassifier::updateClassification(int32_t deviceId, nsecs_t eventTime,
231 MotionClassification classification) {
232 std::scoped_lock lock(mLock);
233 const nsecs_t lastDownTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
234 if (eventTime < lastDownTime) {
235 // HAL just finished processing an event that belonged to an earlier gesture,
236 // but new gesture is already in progress. Drop this classification.
237 ALOGW("Received late classification. Late by at least %" PRId64 " ms.",
238 nanoseconds_to_milliseconds(lastDownTime - eventTime));
239 return;
240 }
241 mClassifications[deviceId] = classification;
242}
243
244void MotionClassifier::setClassification(int32_t deviceId, MotionClassification classification) {
245 std::scoped_lock lock(mLock);
246 mClassifications[deviceId] = classification;
247}
248
249void MotionClassifier::clearClassifications() {
250 std::scoped_lock lock(mLock);
251 mClassifications.clear();
252}
253
254MotionClassification MotionClassifier::getClassification(int32_t deviceId) {
255 std::scoped_lock lock(mLock);
256 return getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
257}
258
259void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) {
260 std::scoped_lock lock(mLock);
261 mLastDownTimes[deviceId] = downTime;
262 mClassifications[deviceId] = MotionClassification::NONE;
263}
264
Siarhei Vishniakoue3021d72020-02-28 15:25:41 -0800265void MotionClassifier::clearDeviceState(int32_t deviceId) {
266 std::scoped_lock lock(mLock);
267 mClassifications.erase(deviceId);
268 mLastDownTimes.erase(deviceId);
269}
270
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800271MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800272 if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
273 updateLastDownTime(args.deviceId, args.downTime);
274 }
275
276 ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args));
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800277 enqueueEvent(std::move(event));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800278 return getClassification(args.deviceId);
279}
280
281void MotionClassifier::reset() {
282 mEvents.clear();
283 mEvents.push(ClassifierEvent::createHalResetEvent());
284}
285
286/**
287 * Per-device reset. Clear the outstanding events that are going to be sent to HAL.
288 * Request InputClassifier thread to call resetDevice for this particular device.
289 */
290void MotionClassifier::reset(const NotifyDeviceResetArgs& args) {
291 int32_t deviceId = args.deviceId;
292 // Clear the pending events right away, to avoid unnecessary work done by the HAL.
293 mEvents.erase([deviceId](const ClassifierEvent& event) {
294 std::optional<int32_t> eventDeviceId = event.getDeviceId();
295 return eventDeviceId && (*eventDeviceId == deviceId);
296 });
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800297 enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800298}
299
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700300const char* MotionClassifier::getServiceStatus() REQUIRES(mLock) {
301 if (!mService) {
302 return "null";
303 }
304 if (mService->ping().isOk()) {
305 return "running";
306 }
307 return "not responding";
308}
309
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800310void MotionClassifier::dump(std::string& dump) {
311 std::scoped_lock lock(mLock);
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700312 dump += StringPrintf(INDENT2 "mService status: %s\n", getServiceStatus());
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800313 dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n",
314 mEvents.size(), MAX_EVENTS);
315 dump += INDENT2 "mClassifications, mLastDownTimes:\n";
316 dump += INDENT3 "Device Id\tClassification\tLast down time";
317 // Combine mClassifications and mLastDownTimes into a single table.
318 // Create a superset of device ids.
319 std::unordered_set<int32_t> deviceIds;
320 std::for_each(mClassifications.begin(), mClassifications.end(),
321 [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
322 std::for_each(mLastDownTimes.begin(), mLastDownTimes.end(),
323 [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
324 for(int32_t deviceId : deviceIds) {
325 const MotionClassification classification =
326 getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
327 const nsecs_t downTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
328 dump += StringPrintf("\n" INDENT4 "%" PRId32 "\t%s\t%" PRId64,
329 deviceId, motionClassificationToString(classification), downTime);
330 }
331}
332
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800333// --- HalDeathRecipient
334
335InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {}
336
337void InputClassifier::HalDeathRecipient::serviceDied(
338 uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) {
339 sp<android::hidl::base::V1_0::IBase> service = who.promote();
340 if (service) {
341 service->unlinkToDeath(this);
342 }
343 mParent.setMotionClassifier(nullptr);
344}
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800345
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800346// --- InputClassifier ---
347
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800348InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
Siarhei Vishniakouc9ac19e2020-03-19 11:55:01 -0700349 : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
350
351void InputClassifier::setMotionClassifierEnabled(bool enabled) {
352 if (enabled) {
353 ALOGI("Enabling motion classifier");
354 if (mInitializeMotionClassifierThread.joinable()) {
355 mInitializeMotionClassifierThread.join();
356 }
357 mInitializeMotionClassifierThread = std::thread(
358 [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); });
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800359#if defined(__linux__)
Siarhei Vishniakouc9ac19e2020-03-19 11:55:01 -0700360 // Set the thread name for debugging
361 pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
362 "Create MotionClassifier");
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800363#endif
Siarhei Vishniakouc9ac19e2020-03-19 11:55:01 -0700364 } else {
365 ALOGI("Disabling motion classifier");
366 setMotionClassifier(nullptr);
367 }
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800368}
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800369
370void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
371 // pass through
372 mListener->notifyConfigurationChanged(args);
373}
374
375void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
376 // pass through
377 mListener->notifyKey(args);
378}
379
380void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800381 std::scoped_lock lock(mLock);
382 // MotionClassifier is only used for touch events, for now
383 const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
384 if (!sendToMotionClassifier) {
385 mListener->notifyMotion(args);
386 return;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800387 }
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800388
389 NotifyMotionArgs newArgs(*args);
390 newArgs.classification = mMotionClassifier->classify(newArgs);
391 mListener->notifyMotion(&newArgs);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800392}
393
394void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
395 // pass through
396 mListener->notifySwitch(args);
397}
398
399void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800400 std::scoped_lock lock(mLock);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800401 if (mMotionClassifier) {
402 mMotionClassifier->reset(*args);
403 }
404 // continue to next stage
405 mListener->notifyDeviceReset(args);
406}
407
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800408void InputClassifier::setMotionClassifier(
409 std::unique_ptr<MotionClassifierInterface> motionClassifier) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800410 std::scoped_lock lock(mLock);
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800411 mMotionClassifier = std::move(motionClassifier);
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800412}
413
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800414void InputClassifier::dump(std::string& dump) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800415 std::scoped_lock lock(mLock);
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800416 dump += "Input Classifier State:\n";
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800417 dump += INDENT1 "Motion Classifier:\n";
418 if (mMotionClassifier) {
419 mMotionClassifier->dump(dump);
420 } else {
421 dump += INDENT2 "<nullptr>";
422 }
423 dump += "\n";
424}
425
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800426InputClassifier::~InputClassifier() {
Siarhei Vishniakouc9ac19e2020-03-19 11:55:01 -0700427 if (mInitializeMotionClassifierThread.joinable()) {
428 mInitializeMotionClassifierThread.join();
429 }
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800430}
431
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700432} // namespace android