1 /*
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"
20 #include "InputClassifierConverter.h"
21
22 #include <algorithm>
23 #include <android-base/stringprintf.h>
24 #include <cmath>
25 #include <inttypes.h>
26 #include <log/log.h>
27 #if defined(__linux__)
28 #include <pthread.h>
29 #endif
30 #include <unordered_set>
31
32 #include <android/hardware/input/classifier/1.0/IInputClassifier.h>
33
34 #define INDENT1 " "
35 #define INDENT2 " "
36 #define INDENT3 " "
37 #define INDENT4 " "
38 #define INDENT5 " "
39
40 using android::base::StringPrintf;
41 using android::hardware::hidl_bitfield;
42 using android::hardware::hidl_vec;
43 using android::hardware::Return;
44 using namespace android::hardware::input;
45
46 namespace android {
47
48 //Max number of elements to store in mEvents.
49 static constexpr size_t MAX_EVENTS = 5;
50
51 template<class K, class V>
getValueForKey(const std::unordered_map<K,V> & map,K key,V defaultValue)52 static 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
getMotionClassification(common::V1_0::Classification classification)60 static 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
isTouchEvent(const NotifyMotionArgs & args)70 static bool isTouchEvent(const NotifyMotionArgs& args) {
71 return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
72 }
73
74 // --- ClassifierEvent ---
75
ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args)76 ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
77 type(ClassifierEventType::MOTION), args(std::move(args)) { };
ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args)78 ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args) :
79 type(ClassifierEventType::DEVICE_RESET), args(std::move(args)) { };
ClassifierEvent(ClassifierEventType type,std::unique_ptr<NotifyArgs> args)80 ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args) :
81 type(type), args(std::move(args)) { };
82
ClassifierEvent(ClassifierEvent && other)83 ClassifierEvent::ClassifierEvent(ClassifierEvent&& other) :
84 type(other.type), args(std::move(other.args)) { };
85
operator =(ClassifierEvent && other)86 ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) {
87 type = other.type;
88 args = std::move(other.args);
89 return *this;
90 }
91
createHalResetEvent()92 ClassifierEvent ClassifierEvent::createHalResetEvent() {
93 return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr);
94 }
95
createExitEvent()96 ClassifierEvent ClassifierEvent::createExitEvent() {
97 return ClassifierEvent(ClassifierEventType::EXIT, nullptr);
98 }
99
getDeviceId() const100 std::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
MotionClassifier(sp<android::hardware::input::classifier::V1_0::IInputClassifier> service)122 MotionClassifier::MotionClassifier(
123 sp<android::hardware::input::classifier::V1_0::IInputClassifier> service)
124 : mEvents(MAX_EVENTS), mService(service) {
125 // 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());
130
131 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
create(sp<android::hardware::hidl_death_recipient> deathRecipient)138 std::unique_ptr<MotionClassifierInterface> MotionClassifier::create(
139 sp<android::hardware::hidl_death_recipient> deathRecipient) {
140 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));
156 }
157
~MotionClassifier()158 MotionClassifier::~MotionClassifier() {
159 requestExit();
160 mHalThread.join();
161 }
162
163 /**
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 */
processEvents()173 void MotionClassifier::processEvents() {
174 while (true) {
175 ClassifierEvent event = mEvents.pop();
176 bool halResponseOk = true;
177 switch (event.type) {
178 case ClassifierEventType::MOTION: {
179 NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
180 common::V1_0::MotionEvent motionEvent =
181 notifyMotionArgsToHalMotionEvent(*motionArgs);
182 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 }
189 break;
190 }
191 case ClassifierEventType::DEVICE_RESET: {
192 const int32_t deviceId = *(event.getDeviceId());
193 halResponseOk = mService->resetDevice(deviceId).isOk();
194 clearDeviceState(deviceId);
195 break;
196 }
197 case ClassifierEventType::HAL_RESET: {
198 halResponseOk = mService->reset().isOk();
199 clearClassifications();
200 break;
201 }
202 case ClassifierEventType::EXIT: {
203 clearClassifications();
204 return;
205 }
206 }
207 if (!halResponseOk) {
208 ALOGE("Error communicating with InputClassifier HAL. "
209 "Exiting MotionClassifier HAL thread");
210 clearClassifications();
211 return;
212 }
213 }
214 }
215
enqueueEvent(ClassifierEvent && event)216 void 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.
220 ALOGE("Could not add the event to the queue. Resetting");
221 reset();
222 }
223 }
224
requestExit()225 void MotionClassifier::requestExit() {
226 reset();
227 mEvents.push(ClassifierEvent::createExitEvent());
228 }
229
updateClassification(int32_t deviceId,nsecs_t eventTime,MotionClassification classification)230 void 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
setClassification(int32_t deviceId,MotionClassification classification)244 void MotionClassifier::setClassification(int32_t deviceId, MotionClassification classification) {
245 std::scoped_lock lock(mLock);
246 mClassifications[deviceId] = classification;
247 }
248
clearClassifications()249 void MotionClassifier::clearClassifications() {
250 std::scoped_lock lock(mLock);
251 mClassifications.clear();
252 }
253
getClassification(int32_t deviceId)254 MotionClassification MotionClassifier::getClassification(int32_t deviceId) {
255 std::scoped_lock lock(mLock);
256 return getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
257 }
258
updateLastDownTime(int32_t deviceId,nsecs_t downTime)259 void 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
clearDeviceState(int32_t deviceId)265 void MotionClassifier::clearDeviceState(int32_t deviceId) {
266 std::scoped_lock lock(mLock);
267 mClassifications.erase(deviceId);
268 mLastDownTimes.erase(deviceId);
269 }
270
classify(const NotifyMotionArgs & args)271 MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
272 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));
277 enqueueEvent(std::move(event));
278 return getClassification(args.deviceId);
279 }
280
reset()281 void 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 */
reset(const NotifyDeviceResetArgs & args)290 void 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 });
297 enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args));
298 }
299
getServiceStatus()300 const 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
dump(std::string & dump)310 void MotionClassifier::dump(std::string& dump) {
311 std::scoped_lock lock(mLock);
312 dump += StringPrintf(INDENT2 "mService status: %s\n", getServiceStatus());
313 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
333 // --- HalDeathRecipient
334
HalDeathRecipient(InputClassifier & parent)335 InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {}
336
serviceDied(uint64_t cookie,const wp<android::hidl::base::V1_0::IBase> & who)337 void 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 }
345
346 // --- InputClassifier ---
347
InputClassifier(const sp<InputListenerInterface> & listener)348 InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
349 : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
350
setMotionClassifierEnabled(bool enabled)351 void 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)); });
359 #if defined(__linux__)
360 // Set the thread name for debugging
361 pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
362 "Create MotionClassifier");
363 #endif
364 } else {
365 ALOGI("Disabling motion classifier");
366 setMotionClassifier(nullptr);
367 }
368 }
369
notifyConfigurationChanged(const NotifyConfigurationChangedArgs * args)370 void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
371 // pass through
372 mListener->notifyConfigurationChanged(args);
373 }
374
notifyKey(const NotifyKeyArgs * args)375 void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
376 // pass through
377 mListener->notifyKey(args);
378 }
379
notifyMotion(const NotifyMotionArgs * args)380 void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
381 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;
387 }
388
389 NotifyMotionArgs newArgs(*args);
390 newArgs.classification = mMotionClassifier->classify(newArgs);
391 mListener->notifyMotion(&newArgs);
392 }
393
notifySwitch(const NotifySwitchArgs * args)394 void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
395 // pass through
396 mListener->notifySwitch(args);
397 }
398
notifyDeviceReset(const NotifyDeviceResetArgs * args)399 void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
400 std::scoped_lock lock(mLock);
401 if (mMotionClassifier) {
402 mMotionClassifier->reset(*args);
403 }
404 // continue to next stage
405 mListener->notifyDeviceReset(args);
406 }
407
setMotionClassifier(std::unique_ptr<MotionClassifierInterface> motionClassifier)408 void InputClassifier::setMotionClassifier(
409 std::unique_ptr<MotionClassifierInterface> motionClassifier) {
410 std::scoped_lock lock(mLock);
411 mMotionClassifier = std::move(motionClassifier);
412 }
413
dump(std::string & dump)414 void InputClassifier::dump(std::string& dump) {
415 std::scoped_lock lock(mLock);
416 dump += "Input Classifier State:\n";
417 dump += INDENT1 "Motion Classifier:\n";
418 if (mMotionClassifier) {
419 mMotionClassifier->dump(dump);
420 } else {
421 dump += INDENT2 "<nullptr>";
422 }
423 dump += "\n";
424 }
425
~InputClassifier()426 InputClassifier::~InputClassifier() {
427 if (mInitializeMotionClassifierThread.joinable()) {
428 mInitializeMotionClassifierThread.join();
429 }
430 }
431
432 } // namespace android
433