blob: 8ba1f7f8c143320d1ff566d88d7bce8cea903364 [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
30#include <server_configurable_flags/get_flags.h>
Siarhei Vishniakoua028c442019-02-04 14:33:23 -080031#include <unordered_set>
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080032
33#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
34
Siarhei Vishniakoua028c442019-02-04 14:33:23 -080035#define INDENT1 " "
36#define INDENT2 " "
37#define INDENT3 " "
38#define INDENT4 " "
39#define INDENT5 " "
40
41using android::base::StringPrintf;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080042using android::hardware::hidl_bitfield;
43using android::hardware::hidl_vec;
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -080044using android::hardware::Return;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080045using namespace android::hardware::input;
46
47namespace android {
48
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080049// Category (=namespace) name for the input settings that are applied at boot time
50static const char* INPUT_NATIVE_BOOT = "input_native_boot";
51// Feature flag name for the deep press feature
52static const char* DEEP_PRESS_ENABLED = "deep_press_enabled";
53
54//Max number of elements to store in mEvents.
55static constexpr size_t MAX_EVENTS = 5;
56
57template<class K, class V>
58static V getValueForKey(const std::unordered_map<K, V>& map, K key, V defaultValue) {
59 auto it = map.find(key);
60 if (it == map.end()) {
61 return defaultValue;
62 }
63 return it->second;
64}
65
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080066static MotionClassification getMotionClassification(common::V1_0::Classification classification) {
67 static_assert(MotionClassification::NONE ==
68 static_cast<MotionClassification>(common::V1_0::Classification::NONE));
69 static_assert(MotionClassification::AMBIGUOUS_GESTURE ==
70 static_cast<MotionClassification>(common::V1_0::Classification::AMBIGUOUS_GESTURE));
71 static_assert(MotionClassification::DEEP_PRESS ==
72 static_cast<MotionClassification>(common::V1_0::Classification::DEEP_PRESS));
73 return static_cast<MotionClassification>(classification);
74}
75
76static bool isTouchEvent(const NotifyMotionArgs& args) {
77 return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
78}
79
80// Check if the "deep touch" feature is on.
81static bool deepPressEnabled() {
82 std::string flag_value = server_configurable_flags::GetServerConfigurableFlag(
Philip Quinn64f1c162019-12-09 12:08:44 -080083 INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true");
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080084 std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower);
85 if (flag_value == "1" || flag_value == "true") {
86 ALOGI("Deep press feature enabled.");
87 return true;
88 }
89 ALOGI("Deep press feature is not enabled.");
90 return false;
91}
92
93
94// --- ClassifierEvent ---
95
96ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
97 type(ClassifierEventType::MOTION), args(std::move(args)) { };
98ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args) :
99 type(ClassifierEventType::DEVICE_RESET), args(std::move(args)) { };
100ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args) :
101 type(type), args(std::move(args)) { };
102
103ClassifierEvent::ClassifierEvent(ClassifierEvent&& other) :
104 type(other.type), args(std::move(other.args)) { };
105
106ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) {
107 type = other.type;
108 args = std::move(other.args);
109 return *this;
110}
111
112ClassifierEvent ClassifierEvent::createHalResetEvent() {
113 return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr);
114}
115
116ClassifierEvent ClassifierEvent::createExitEvent() {
117 return ClassifierEvent(ClassifierEventType::EXIT, nullptr);
118}
119
120std::optional<int32_t> ClassifierEvent::getDeviceId() const {
121 switch (type) {
122 case ClassifierEventType::MOTION: {
123 NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(args.get());
124 return motionArgs->deviceId;
125 }
126 case ClassifierEventType::DEVICE_RESET: {
127 NotifyDeviceResetArgs* deviceResetArgs =
128 static_cast<NotifyDeviceResetArgs*>(args.get());
129 return deviceResetArgs->deviceId;
130 }
131 case ClassifierEventType::HAL_RESET: {
132 return std::nullopt;
133 }
134 case ClassifierEventType::EXIT: {
135 return std::nullopt;
136 }
137 }
138}
139
140// --- MotionClassifier ---
141
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800142MotionClassifier::MotionClassifier(
143 sp<android::hardware::input::classifier::V1_0::IInputClassifier> service)
144 : mEvents(MAX_EVENTS), mService(service) {
Siarhei Vishniakou6dbc3f62019-02-04 14:30:11 -0800145 // Under normal operation, we do not need to reset the HAL here. But in the case where system
146 // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
147 // have received events in the past. That means, that HAL could be in an inconsistent state
148 // once it receives events from the newly created MotionClassifier.
149 mEvents.push(ClassifierEvent::createHalResetEvent());
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700150
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800151 mHalThread = std::thread(&MotionClassifier::processEvents, this);
152#if defined(__linux__)
153 // Set the thread name for debugging
154 pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
155#endif
156}
157
158std::unique_ptr<MotionClassifierInterface> MotionClassifier::create(
159 sp<android::hardware::hidl_death_recipient> deathRecipient) {
160 if (!deepPressEnabled()) {
161 // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work.
162 // When MotionClassifier is null, InputClassifier will forward all events
163 // to the next InputListener, unmodified.
164 return nullptr;
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700165 }
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800166 sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
167 classifier::V1_0::IInputClassifier::getService();
168 if (!service) {
169 // Not really an error, maybe the device does not have this HAL,
170 // but somehow the feature flag is flipped
171 ALOGI("Could not obtain InputClassifier HAL");
172 return nullptr;
173 }
174
175 const bool linked = service->linkToDeath(deathRecipient, 0 /* cookie */).withDefault(false);
176 if (!linked) {
177 ALOGE("Could not link death recipient to the HAL death");
178 return nullptr;
179 }
180 // Using 'new' to access a non-public constructor
181 return std::unique_ptr<MotionClassifier>(new MotionClassifier(service));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800182}
183
184MotionClassifier::~MotionClassifier() {
185 requestExit();
186 mHalThread.join();
187}
188
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800189/**
190 * Obtain the classification from the HAL for a given MotionEvent.
191 * Should only be called from the InputClassifier thread (mHalThread).
192 * Should not be called from the thread that notifyMotion runs on.
193 *
194 * There is no way to provide a timeout for a HAL call. So if the HAL takes too long
195 * to return a classification, this would directly impact the touch latency.
196 * To remove any possibility of negatively affecting the touch latency, the HAL
197 * is called from a dedicated thread.
198 */
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800199void MotionClassifier::processEvents() {
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800200 while (true) {
201 ClassifierEvent event = mEvents.pop();
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800202 bool halResponseOk = true;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800203 switch (event.type) {
204 case ClassifierEventType::MOTION: {
205 NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
Siarhei Vishniakoua47a4d42019-05-06 17:14:11 -0700206 common::V1_0::MotionEvent motionEvent =
207 notifyMotionArgsToHalMotionEvent(*motionArgs);
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800208 Return<common::V1_0::Classification> response = mService->classify(motionEvent);
209 halResponseOk = response.isOk();
210 if (halResponseOk) {
211 common::V1_0::Classification halClassification = response;
212 updateClassification(motionArgs->deviceId, motionArgs->eventTime,
213 getMotionClassification(halClassification));
214 }
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800215 break;
216 }
217 case ClassifierEventType::DEVICE_RESET: {
218 const int32_t deviceId = *(event.getDeviceId());
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800219 halResponseOk = mService->resetDevice(deviceId).isOk();
Siarhei Vishniakoue3021d72020-02-28 15:25:41 -0800220 clearDeviceState(deviceId);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800221 break;
222 }
223 case ClassifierEventType::HAL_RESET: {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800224 halResponseOk = mService->reset().isOk();
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800225 clearClassifications();
226 break;
227 }
228 case ClassifierEventType::EXIT: {
229 clearClassifications();
230 return;
231 }
232 }
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800233 if (!halResponseOk) {
234 ALOGE("Error communicating with InputClassifier HAL. "
235 "Exiting MotionClassifier HAL thread");
236 clearClassifications();
237 return;
238 }
239 }
240}
241
242void MotionClassifier::enqueueEvent(ClassifierEvent&& event) {
243 bool eventAdded = mEvents.push(std::move(event));
244 if (!eventAdded) {
245 // If the queue is full, suspect the HAL is slow in processing the events.
Siarhei Vishniakou9b5a8212019-07-01 14:25:40 -0700246 ALOGE("Could not add the event to the queue. Resetting");
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800247 reset();
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800248 }
249}
250
251void MotionClassifier::requestExit() {
252 reset();
253 mEvents.push(ClassifierEvent::createExitEvent());
254}
255
256void MotionClassifier::updateClassification(int32_t deviceId, nsecs_t eventTime,
257 MotionClassification classification) {
258 std::scoped_lock lock(mLock);
259 const nsecs_t lastDownTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
260 if (eventTime < lastDownTime) {
261 // HAL just finished processing an event that belonged to an earlier gesture,
262 // but new gesture is already in progress. Drop this classification.
263 ALOGW("Received late classification. Late by at least %" PRId64 " ms.",
264 nanoseconds_to_milliseconds(lastDownTime - eventTime));
265 return;
266 }
267 mClassifications[deviceId] = classification;
268}
269
270void MotionClassifier::setClassification(int32_t deviceId, MotionClassification classification) {
271 std::scoped_lock lock(mLock);
272 mClassifications[deviceId] = classification;
273}
274
275void MotionClassifier::clearClassifications() {
276 std::scoped_lock lock(mLock);
277 mClassifications.clear();
278}
279
280MotionClassification MotionClassifier::getClassification(int32_t deviceId) {
281 std::scoped_lock lock(mLock);
282 return getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
283}
284
285void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) {
286 std::scoped_lock lock(mLock);
287 mLastDownTimes[deviceId] = downTime;
288 mClassifications[deviceId] = MotionClassification::NONE;
289}
290
Siarhei Vishniakoue3021d72020-02-28 15:25:41 -0800291void MotionClassifier::clearDeviceState(int32_t deviceId) {
292 std::scoped_lock lock(mLock);
293 mClassifications.erase(deviceId);
294 mLastDownTimes.erase(deviceId);
295}
296
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800297MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800298 if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
299 updateLastDownTime(args.deviceId, args.downTime);
300 }
301
302 ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args));
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800303 enqueueEvent(std::move(event));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800304 return getClassification(args.deviceId);
305}
306
307void MotionClassifier::reset() {
308 mEvents.clear();
309 mEvents.push(ClassifierEvent::createHalResetEvent());
310}
311
312/**
313 * Per-device reset. Clear the outstanding events that are going to be sent to HAL.
314 * Request InputClassifier thread to call resetDevice for this particular device.
315 */
316void MotionClassifier::reset(const NotifyDeviceResetArgs& args) {
317 int32_t deviceId = args.deviceId;
318 // Clear the pending events right away, to avoid unnecessary work done by the HAL.
319 mEvents.erase([deviceId](const ClassifierEvent& event) {
320 std::optional<int32_t> eventDeviceId = event.getDeviceId();
321 return eventDeviceId && (*eventDeviceId == deviceId);
322 });
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800323 enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800324}
325
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700326const char* MotionClassifier::getServiceStatus() REQUIRES(mLock) {
327 if (!mService) {
328 return "null";
329 }
330 if (mService->ping().isOk()) {
331 return "running";
332 }
333 return "not responding";
334}
335
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800336void MotionClassifier::dump(std::string& dump) {
337 std::scoped_lock lock(mLock);
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700338 dump += StringPrintf(INDENT2 "mService status: %s\n", getServiceStatus());
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800339 dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n",
340 mEvents.size(), MAX_EVENTS);
341 dump += INDENT2 "mClassifications, mLastDownTimes:\n";
342 dump += INDENT3 "Device Id\tClassification\tLast down time";
343 // Combine mClassifications and mLastDownTimes into a single table.
344 // Create a superset of device ids.
345 std::unordered_set<int32_t> deviceIds;
346 std::for_each(mClassifications.begin(), mClassifications.end(),
347 [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
348 std::for_each(mLastDownTimes.begin(), mLastDownTimes.end(),
349 [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
350 for(int32_t deviceId : deviceIds) {
351 const MotionClassification classification =
352 getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
353 const nsecs_t downTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
354 dump += StringPrintf("\n" INDENT4 "%" PRId32 "\t%s\t%" PRId64,
355 deviceId, motionClassificationToString(classification), downTime);
356 }
357}
358
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800359// --- HalDeathRecipient
360
361InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {}
362
363void InputClassifier::HalDeathRecipient::serviceDied(
364 uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) {
365 sp<android::hidl::base::V1_0::IBase> service = who.promote();
366 if (service) {
367 service->unlinkToDeath(this);
368 }
369 mParent.setMotionClassifier(nullptr);
370}
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800371
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800372// --- InputClassifier ---
373
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800374InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
375 : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {
376 mInitializeMotionClassifierThread = std::thread(
377 [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); });
378#if defined(__linux__)
379 // Set the thread name for debugging
380 pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
381 "Create MotionClassifier");
382#endif
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800383}
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800384
385void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
386 // pass through
387 mListener->notifyConfigurationChanged(args);
388}
389
390void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
391 // pass through
392 mListener->notifyKey(args);
393}
394
395void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800396 std::scoped_lock lock(mLock);
397 // MotionClassifier is only used for touch events, for now
398 const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
399 if (!sendToMotionClassifier) {
400 mListener->notifyMotion(args);
401 return;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800402 }
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800403
404 NotifyMotionArgs newArgs(*args);
405 newArgs.classification = mMotionClassifier->classify(newArgs);
406 mListener->notifyMotion(&newArgs);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800407}
408
409void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
410 // pass through
411 mListener->notifySwitch(args);
412}
413
414void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800415 std::scoped_lock lock(mLock);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800416 if (mMotionClassifier) {
417 mMotionClassifier->reset(*args);
418 }
419 // continue to next stage
420 mListener->notifyDeviceReset(args);
421}
422
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800423void InputClassifier::setMotionClassifier(
424 std::unique_ptr<MotionClassifierInterface> motionClassifier) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800425 std::scoped_lock lock(mLock);
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800426 mMotionClassifier = std::move(motionClassifier);
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800427}
428
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800429void InputClassifier::dump(std::string& dump) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800430 std::scoped_lock lock(mLock);
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800431 dump += "Input Classifier State:\n";
Siarhei Vishniakoue3021d72020-02-28 15:25:41 -0800432 dump += StringPrintf(INDENT1 "Deep press: %s\n", deepPressEnabled() ? "enabled" : "disabled");
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800433
434 dump += INDENT1 "Motion Classifier:\n";
435 if (mMotionClassifier) {
436 mMotionClassifier->dump(dump);
437 } else {
438 dump += INDENT2 "<nullptr>";
439 }
440 dump += "\n";
441}
442
Siarhei Vishniakou16523972020-03-04 17:48:39 -0800443InputClassifier::~InputClassifier() {
444 mInitializeMotionClassifierThread.join();
445}
446
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700447} // namespace android