blob: 6e51fb2717942ec48114f8e4867def623d1f1c6b [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 Vishniakouf48bb9e2020-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 Vishniakouf48bb9e2020-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 Vishniakouf48bb9e2020-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 Vishniakouf48bb9e2020-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 Vishniakou473174e2017-12-27 16:44:42 -0800220 setClassification(deviceId, MotionClassification::NONE);
221 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.
246 ALOGE("Dropped event with eventTime %" PRId64, event.args->eventTime);
247 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
291MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800292 if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
293 updateLastDownTime(args.deviceId, args.downTime);
294 }
295
296 ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args));
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800297 enqueueEvent(std::move(event));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800298 return getClassification(args.deviceId);
299}
300
301void MotionClassifier::reset() {
302 mEvents.clear();
303 mEvents.push(ClassifierEvent::createHalResetEvent());
304}
305
306/**
307 * Per-device reset. Clear the outstanding events that are going to be sent to HAL.
308 * Request InputClassifier thread to call resetDevice for this particular device.
309 */
310void MotionClassifier::reset(const NotifyDeviceResetArgs& args) {
311 int32_t deviceId = args.deviceId;
312 // Clear the pending events right away, to avoid unnecessary work done by the HAL.
313 mEvents.erase([deviceId](const ClassifierEvent& event) {
314 std::optional<int32_t> eventDeviceId = event.getDeviceId();
315 return eventDeviceId && (*eventDeviceId == deviceId);
316 });
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800317 enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args));
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800318}
319
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700320const char* MotionClassifier::getServiceStatus() REQUIRES(mLock) {
321 if (!mService) {
322 return "null";
323 }
324 if (mService->ping().isOk()) {
325 return "running";
326 }
327 return "not responding";
328}
329
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800330void MotionClassifier::dump(std::string& dump) {
331 std::scoped_lock lock(mLock);
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700332 dump += StringPrintf(INDENT2 "mService status: %s\n", getServiceStatus());
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800333 dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n",
334 mEvents.size(), MAX_EVENTS);
335 dump += INDENT2 "mClassifications, mLastDownTimes:\n";
336 dump += INDENT3 "Device Id\tClassification\tLast down time";
337 // Combine mClassifications and mLastDownTimes into a single table.
338 // Create a superset of device ids.
339 std::unordered_set<int32_t> deviceIds;
340 std::for_each(mClassifications.begin(), mClassifications.end(),
341 [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
342 std::for_each(mLastDownTimes.begin(), mLastDownTimes.end(),
343 [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
344 for(int32_t deviceId : deviceIds) {
345 const MotionClassification classification =
346 getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
347 const nsecs_t downTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
348 dump += StringPrintf("\n" INDENT4 "%" PRId32 "\t%s\t%" PRId64,
349 deviceId, motionClassificationToString(classification), downTime);
350 }
351}
352
Siarhei Vishniakouf48bb9e2020-03-04 17:48:39 -0800353// --- HalDeathRecipient
354
355InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {}
356
357void InputClassifier::HalDeathRecipient::serviceDied(
358 uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) {
359 sp<android::hidl::base::V1_0::IBase> service = who.promote();
360 if (service) {
361 service->unlinkToDeath(this);
362 }
363 mParent.setMotionClassifier(nullptr);
364}
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800365
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800366// --- InputClassifier ---
367
Siarhei Vishniakouf48bb9e2020-03-04 17:48:39 -0800368InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
369 : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {
370 mInitializeMotionClassifierThread = std::thread(
371 [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); });
372#if defined(__linux__)
373 // Set the thread name for debugging
374 pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
375 "Create MotionClassifier");
376#endif
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800377}
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800378
379void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
380 // pass through
381 mListener->notifyConfigurationChanged(args);
382}
383
384void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
385 // pass through
386 mListener->notifyKey(args);
387}
388
389void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800390 std::scoped_lock lock(mLock);
391 // MotionClassifier is only used for touch events, for now
392 const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
393 if (!sendToMotionClassifier) {
394 mListener->notifyMotion(args);
395 return;
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800396 }
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800397
398 NotifyMotionArgs newArgs(*args);
399 newArgs.classification = mMotionClassifier->classify(newArgs);
400 mListener->notifyMotion(&newArgs);
Siarhei Vishniakou473174e2017-12-27 16:44:42 -0800401}
402
403void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
404 // pass through
405 mListener->notifySwitch(args);
406}
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
414 mListener->notifyDeviceReset(args);
415}
416
Siarhei Vishniakouf48bb9e2020-03-04 17:48:39 -0800417void InputClassifier::setMotionClassifier(
418 std::unique_ptr<MotionClassifierInterface> motionClassifier) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800419 std::scoped_lock lock(mLock);
Siarhei Vishniakouf48bb9e2020-03-04 17:48:39 -0800420 mMotionClassifier = std::move(motionClassifier);
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800421}
422
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800423void InputClassifier::dump(std::string& dump) {
Siarhei Vishniakou15b66d12019-02-04 14:27:29 -0800424 std::scoped_lock lock(mLock);
Siarhei Vishniakoua028c442019-02-04 14:33:23 -0800425 dump += "Input Classifier State:\n";
426
427 dump += INDENT1 "Motion Classifier:\n";
428 if (mMotionClassifier) {
429 mMotionClassifier->dump(dump);
430 } else {
431 dump += INDENT2 "<nullptr>";
432 }
433 dump += "\n";
434}
435
Siarhei Vishniakouf48bb9e2020-03-04 17:48:39 -0800436InputClassifier::~InputClassifier() {
437 mInitializeMotionClassifierThread.join();
438}
439
Siarhei Vishniakou4bdbb6a2019-04-11 09:42:09 -0700440} // namespace android