blob: ef1a2247e93eb0e84b959ccf587355ee3edc83fc [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
49static constexpr bool DEBUG = false;
50
51// Category (=namespace) name for the input settings that are applied at boot time
52static const char* INPUT_NATIVE_BOOT = "input_native_boot";
53// Feature flag name for the deep press feature
54static const char* DEEP_PRESS_ENABLED = "deep_press_enabled";
55
56//Max number of elements to store in mEvents.
57static constexpr size_t MAX_EVENTS = 5;
58
59template<class K, class V>
60static V getValueForKey(const std::unordered_map<K, V>& map, K key, V defaultValue) {
61 auto it = map.find(key);
62 if (it == map.end()) {
63 return defaultValue;
64 }
65 return it->second;
66}
67
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080068static MotionClassification getMotionClassification(common::V1_0::Classification classification) {
69 static_assert(MotionClassification::NONE ==
70 static_cast<MotionClassification>(common::V1_0::Classification::NONE));
71 static_assert(MotionClassification::AMBIGUOUS_GESTURE ==
72 static_cast<MotionClassification>(common::V1_0::Classification::AMBIGUOUS_GESTURE));
73 static_assert(MotionClassification::DEEP_PRESS ==
74 static_cast<MotionClassification>(common::V1_0::Classification::DEEP_PRESS));
75 return static_cast<MotionClassification>(classification);
76}
77
78static bool isTouchEvent(const NotifyMotionArgs& args) {
79 return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
80}
81
82// Check if the "deep touch" feature is on.
83static bool deepPressEnabled() {
84 std::string flag_value = server_configurable_flags::GetServerConfigurableFlag(
Philip Quinn64f1c162019-12-09 12:08:44 -080085 INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true");
Siarhei Vishniakou473174e2017-12-27 16:44:42 -080086 std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower);
87 if (flag_value == "1" || flag_value == "true") {
88 ALOGI("Deep press feature enabled.");
89 return true;
90 }
91 ALOGI("Deep press feature is not enabled.");
92 return false;
93}
94
95
96// --- ClassifierEvent ---
97
98ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
99 type(ClassifierEventType::MOTION), args(std::move(args)) { };
100ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args) :
101 type(ClassifierEventType::DEVICE_RESET), args(std::move(args)) { };
102ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args) :
103 type(type), args(std::move(args)) { };
104
105ClassifierEvent::ClassifierEvent(ClassifierEvent&& other) :
106 type(other.type), args(std::move(other.args)) { };
107
108ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) {
109 type = other.type;
110 args = std::move(other.args);
111 return *this;
112}
113
114ClassifierEvent ClassifierEvent::createHalResetEvent() {
115 return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr);
116}
117
118ClassifierEvent ClassifierEvent::createExitEvent() {
119 return ClassifierEvent(ClassifierEventType::EXIT, nullptr);
120}
121
122std::optional<int32_t> ClassifierEvent::getDeviceId() const {
123 switch (type) {
124 case ClassifierEventType::MOTION: {
125 NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(args.get());
126 return motionArgs->deviceId;
127 }
128 case ClassifierEventType::DEVICE_RESET: {
129 NotifyDeviceResetArgs* deviceResetArgs =
130 static_cast<NotifyDeviceResetArgs*>(args.get());
131 return deviceResetArgs->deviceId;
132 }
133 case ClassifierEventType::HAL_RESET: {
134 return std::nullopt;
135 }
136 case ClassifierEventType::EXIT: {
137 return std::nullopt;
138 }
139 }
140}
141
142// --- MotionClassifier ---
143
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700144MotionClassifier::MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient) :
145 mDeathRecipient(deathRecipient), mEvents(MAX_EVENTS) {
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800146 mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this);
147#if defined(__linux__)
148 // Set the thread name for debugging
149 pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
150#endif
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700151}
152
153/**
154 * This function may block for some time to initialize the HAL, so it should only be called
155 * from the "InputClassifier HAL" thread.
156 */
157bool MotionClassifier::init() {
158 ensureHalThread(__func__);
159 sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
160 classifier::V1_0::IInputClassifier::getService();
161 if (!service) {
162 // Not really an error, maybe the device does not have this HAL,
163 // but somehow the feature flag is flipped
164 ALOGI("Could not obtain InputClassifier HAL");
165 return false;
166 }
167
168 sp<android::hardware::hidl_death_recipient> recipient = mDeathRecipient.promote();
169 if (recipient != nullptr) {
170 const bool linked = service->linkToDeath(recipient, 0 /* cookie */).withDefault(false);
171 if (!linked) {
172 ALOGE("Could not link MotionClassifier to the HAL death");
173 return false;
174 }
175 }
176
Siarhei Vishniakou6dbc3f62019-02-04 14:30:11 -0800177 // Under normal operation, we do not need to reset the HAL here. But in the case where system
178 // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
179 // have received events in the past. That means, that HAL could be in an inconsistent state
180 // once it receives events from the newly created MotionClassifier.
181 mEvents.push(ClassifierEvent::createHalResetEvent());
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700182
183 {
184 std::scoped_lock lock(mLock);
185 if (mService) {
186 ALOGE("MotionClassifier::%s should only be called once", __func__);
187 }
188 mService = service;
189 }
190 return true;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800191}
192
193MotionClassifier::~MotionClassifier() {
194 requestExit();
195 mHalThread.join();
196}
197
198void MotionClassifier::ensureHalThread(const char* function) {
199 if (DEBUG) {
200 if (std::this_thread::get_id() != mHalThread.get_id()) {
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700201 LOG_FATAL("Function %s should only be called from InputClassifier thread", function);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800202 }
203 }
204}
205
206/**
207 * Obtain the classification from the HAL for a given MotionEvent.
208 * Should only be called from the InputClassifier thread (mHalThread).
209 * Should not be called from the thread that notifyMotion runs on.
210 *
211 * There is no way to provide a timeout for a HAL call. So if the HAL takes too long
212 * to return a classification, this would directly impact the touch latency.
213 * To remove any possibility of negatively affecting the touch latency, the HAL
214 * is called from a dedicated thread.
215 */
216void MotionClassifier::callInputClassifierHal() {
217 ensureHalThread(__func__);
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700218 const bool initialized = init();
219 if (!initialized) {
220 // MotionClassifier no longer useful.
221 // Deliver death notification from a separate thread
222 // because ~MotionClassifier may be invoked, which calls mHalThread.join()
223 std::thread([deathRecipient = mDeathRecipient](){
224 sp<android::hardware::hidl_death_recipient> recipient = deathRecipient.promote();
225 if (recipient != nullptr) {
226 recipient->serviceDied(0 /*cookie*/, nullptr);
227 }
228 }).detach();
229 return;
230 }
231 // From this point on, mService is guaranteed to be non-null.
232
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800233 while (true) {
234 ClassifierEvent event = mEvents.pop();
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800235 bool halResponseOk = true;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800236 switch (event.type) {
237 case ClassifierEventType::MOTION: {
238 NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
Siarhei Vishniakoua47a4d42019-05-06 17:14:11 -0700239 common::V1_0::MotionEvent motionEvent =
240 notifyMotionArgsToHalMotionEvent(*motionArgs);
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800241 Return<common::V1_0::Classification> response = mService->classify(motionEvent);
242 halResponseOk = response.isOk();
243 if (halResponseOk) {
244 common::V1_0::Classification halClassification = response;
245 updateClassification(motionArgs->deviceId, motionArgs->eventTime,
246 getMotionClassification(halClassification));
247 }
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800248 break;
249 }
250 case ClassifierEventType::DEVICE_RESET: {
251 const int32_t deviceId = *(event.getDeviceId());
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800252 halResponseOk = mService->resetDevice(deviceId).isOk();
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800253 setClassification(deviceId, MotionClassification::NONE);
254 break;
255 }
256 case ClassifierEventType::HAL_RESET: {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800257 halResponseOk = mService->reset().isOk();
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800258 clearClassifications();
259 break;
260 }
261 case ClassifierEventType::EXIT: {
262 clearClassifications();
263 return;
264 }
265 }
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800266 if (!halResponseOk) {
267 ALOGE("Error communicating with InputClassifier HAL. "
268 "Exiting MotionClassifier HAL thread");
269 clearClassifications();
270 return;
271 }
272 }
273}
274
275void MotionClassifier::enqueueEvent(ClassifierEvent&& event) {
276 bool eventAdded = mEvents.push(std::move(event));
277 if (!eventAdded) {
278 // If the queue is full, suspect the HAL is slow in processing the events.
279 ALOGE("Dropped event with eventTime %" PRId64, event.args->eventTime);
280 reset();
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800281 }
282}
283
284void MotionClassifier::requestExit() {
285 reset();
286 mEvents.push(ClassifierEvent::createExitEvent());
287}
288
289void MotionClassifier::updateClassification(int32_t deviceId, nsecs_t eventTime,
290 MotionClassification classification) {
291 std::scoped_lock lock(mLock);
292 const nsecs_t lastDownTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
293 if (eventTime < lastDownTime) {
294 // HAL just finished processing an event that belonged to an earlier gesture,
295 // but new gesture is already in progress. Drop this classification.
296 ALOGW("Received late classification. Late by at least %" PRId64 " ms.",
297 nanoseconds_to_milliseconds(lastDownTime - eventTime));
298 return;
299 }
300 mClassifications[deviceId] = classification;
301}
302
303void MotionClassifier::setClassification(int32_t deviceId, MotionClassification classification) {
304 std::scoped_lock lock(mLock);
305 mClassifications[deviceId] = classification;
306}
307
308void MotionClassifier::clearClassifications() {
309 std::scoped_lock lock(mLock);
310 mClassifications.clear();
311}
312
313MotionClassification MotionClassifier::getClassification(int32_t deviceId) {
314 std::scoped_lock lock(mLock);
315 return getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
316}
317
318void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) {
319 std::scoped_lock lock(mLock);
320 mLastDownTimes[deviceId] = downTime;
321 mClassifications[deviceId] = MotionClassification::NONE;
322}
323
324MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800325 if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
326 updateLastDownTime(args.deviceId, args.downTime);
327 }
328
329 ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args));
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800330 enqueueEvent(std::move(event));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800331 return getClassification(args.deviceId);
332}
333
334void MotionClassifier::reset() {
335 mEvents.clear();
336 mEvents.push(ClassifierEvent::createHalResetEvent());
337}
338
339/**
340 * Per-device reset. Clear the outstanding events that are going to be sent to HAL.
341 * Request InputClassifier thread to call resetDevice for this particular device.
342 */
343void MotionClassifier::reset(const NotifyDeviceResetArgs& args) {
344 int32_t deviceId = args.deviceId;
345 // Clear the pending events right away, to avoid unnecessary work done by the HAL.
346 mEvents.erase([deviceId](const ClassifierEvent& event) {
347 std::optional<int32_t> eventDeviceId = event.getDeviceId();
348 return eventDeviceId && (*eventDeviceId == deviceId);
349 });
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800350 enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800351}
352
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700353const char* MotionClassifier::getServiceStatus() REQUIRES(mLock) {
354 if (!mService) {
355 return "null";
356 }
357 if (mService->ping().isOk()) {
358 return "running";
359 }
360 return "not responding";
361}
362
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800363void MotionClassifier::dump(std::string& dump) {
364 std::scoped_lock lock(mLock);
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700365 dump += StringPrintf(INDENT2 "mService status: %s\n", getServiceStatus());
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800366 dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n",
367 mEvents.size(), MAX_EVENTS);
368 dump += INDENT2 "mClassifications, mLastDownTimes:\n";
369 dump += INDENT3 "Device Id\tClassification\tLast down time";
370 // Combine mClassifications and mLastDownTimes into a single table.
371 // Create a superset of device ids.
372 std::unordered_set<int32_t> deviceIds;
373 std::for_each(mClassifications.begin(), mClassifications.end(),
374 [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
375 std::for_each(mLastDownTimes.begin(), mLastDownTimes.end(),
376 [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
377 for(int32_t deviceId : deviceIds) {
378 const MotionClassification classification =
379 getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
380 const nsecs_t downTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
381 dump += StringPrintf("\n" INDENT4 "%" PRId32 "\t%s\t%" PRId64,
382 deviceId, motionClassificationToString(classification), downTime);
383 }
384}
385
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800386
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800387// --- InputClassifier ---
388
389InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) :
390 mListener(listener) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800391 // The rest of the initialization is done in onFirstRef, because we need to obtain
392 // an sp to 'this' in order to register for HAL death notifications
393}
394
395void InputClassifier::onFirstRef() {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800396 if (!deepPressEnabled()) {
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700397 // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work.
398 // When MotionClassifier is null, InputClassifier will forward all events
399 // to the next InputListener, unmodified.
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800400 return;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800401 }
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700402 std::scoped_lock lock(mLock);
403 mMotionClassifier = std::make_unique<MotionClassifier>(this);
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800404}
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800405
406void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
407 // pass through
408 mListener->notifyConfigurationChanged(args);
409}
410
411void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
412 // pass through
413 mListener->notifyKey(args);
414}
415
416void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800417 std::scoped_lock lock(mLock);
418 // MotionClassifier is only used for touch events, for now
419 const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
420 if (!sendToMotionClassifier) {
421 mListener->notifyMotion(args);
422 return;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800423 }
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800424
425 NotifyMotionArgs newArgs(*args);
426 newArgs.classification = mMotionClassifier->classify(newArgs);
427 mListener->notifyMotion(&newArgs);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800428}
429
430void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
431 // pass through
432 mListener->notifySwitch(args);
433}
434
435void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800436 std::scoped_lock lock(mLock);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800437 if (mMotionClassifier) {
438 mMotionClassifier->reset(*args);
439 }
440 // continue to next stage
441 mListener->notifyDeviceReset(args);
442}
443
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800444void InputClassifier::serviceDied(uint64_t /*cookie*/,
445 const wp<android::hidl::base::V1_0::IBase>& who) {
446 std::scoped_lock lock(mLock);
447 ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null");
448 mMotionClassifier = nullptr;
449 sp<android::hidl::base::V1_0::IBase> service = who.promote();
450 if (service) {
451 service->unlinkToDeath(this);
452 }
453}
454
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800455void InputClassifier::dump(std::string& dump) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800456 std::scoped_lock lock(mLock);
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800457 dump += "Input Classifier State:\n";
458
459 dump += INDENT1 "Motion Classifier:\n";
460 if (mMotionClassifier) {
461 mMotionClassifier->dump(dump);
462 } else {
463 dump += INDENT2 "<nullptr>";
464 }
465 dump += "\n";
466}
467
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700468} // namespace android