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 setClassification(deviceId, MotionClassification::NONE);
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("Dropped event with eventTime %" PRId64, event.args->eventTime);
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
classify(const NotifyMotionArgs & args)265 MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
266 if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
267 updateLastDownTime(args.deviceId, args.downTime);
268 }
269
270 ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args));
271 enqueueEvent(std::move(event));
272 return getClassification(args.deviceId);
273 }
274
reset()275 void MotionClassifier::reset() {
276 mEvents.clear();
277 mEvents.push(ClassifierEvent::createHalResetEvent());
278 }
279
280 /**
281 * Per-device reset. Clear the outstanding events that are going to be sent to HAL.
282 * Request InputClassifier thread to call resetDevice for this particular device.
283 */
reset(const NotifyDeviceResetArgs & args)284 void MotionClassifier::reset(const NotifyDeviceResetArgs& args) {
285 int32_t deviceId = args.deviceId;
286 // Clear the pending events right away, to avoid unnecessary work done by the HAL.
287 mEvents.erase([deviceId](const ClassifierEvent& event) {
288 std::optional<int32_t> eventDeviceId = event.getDeviceId();
289 return eventDeviceId && (*eventDeviceId == deviceId);
290 });
291 enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args));
292 }
293
getServiceStatus()294 const char* MotionClassifier::getServiceStatus() REQUIRES(mLock) {
295 if (!mService) {
296 return "null";
297 }
298 if (mService->ping().isOk()) {
299 return "running";
300 }
301 return "not responding";
302 }
303
dump(std::string & dump)304 void MotionClassifier::dump(std::string& dump) {
305 std::scoped_lock lock(mLock);
306 dump += StringPrintf(INDENT2 "mService status: %s\n", getServiceStatus());
307 dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n",
308 mEvents.size(), MAX_EVENTS);
309 dump += INDENT2 "mClassifications, mLastDownTimes:\n";
310 dump += INDENT3 "Device Id\tClassification\tLast down time";
311 // Combine mClassifications and mLastDownTimes into a single table.
312 // Create a superset of device ids.
313 std::unordered_set<int32_t> deviceIds;
314 std::for_each(mClassifications.begin(), mClassifications.end(),
315 [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
316 std::for_each(mLastDownTimes.begin(), mLastDownTimes.end(),
317 [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
318 for(int32_t deviceId : deviceIds) {
319 const MotionClassification classification =
320 getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
321 const nsecs_t downTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
322 dump += StringPrintf("\n" INDENT4 "%" PRId32 "\t%s\t%" PRId64,
323 deviceId, motionClassificationToString(classification), downTime);
324 }
325 }
326
327 // --- HalDeathRecipient
328
HalDeathRecipient(InputClassifier & parent)329 InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {}
330
serviceDied(uint64_t cookie,const wp<android::hidl::base::V1_0::IBase> & who)331 void InputClassifier::HalDeathRecipient::serviceDied(
332 uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) {
333 sp<android::hidl::base::V1_0::IBase> service = who.promote();
334 if (service) {
335 service->unlinkToDeath(this);
336 }
337 mParent.setMotionClassifier(nullptr);
338 }
339
340 // --- InputClassifier ---
341
InputClassifier(const sp<InputListenerInterface> & listener)342 InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
343 : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
344
setMotionClassifierEnabled(bool enabled)345 void InputClassifier::setMotionClassifierEnabled(bool enabled) {
346 if (enabled) {
347 ALOGI("Enabling motion classifier");
348 if (mInitializeMotionClassifierThread.joinable()) {
349 mInitializeMotionClassifierThread.join();
350 }
351 mInitializeMotionClassifierThread = std::thread(
352 [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); });
353 #if defined(__linux__)
354 // Set the thread name for debugging
355 pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
356 "Create MotionClassifier");
357 #endif
358 } else {
359 ALOGI("Disabling motion classifier");
360 setMotionClassifier(nullptr);
361 }
362 }
363
notifyConfigurationChanged(const NotifyConfigurationChangedArgs * args)364 void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
365 // pass through
366 mListener->notifyConfigurationChanged(args);
367 }
368
notifyKey(const NotifyKeyArgs * args)369 void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
370 // pass through
371 mListener->notifyKey(args);
372 }
373
notifyMotion(const NotifyMotionArgs * args)374 void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
375 std::scoped_lock lock(mLock);
376 // MotionClassifier is only used for touch events, for now
377 const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
378 if (!sendToMotionClassifier) {
379 mListener->notifyMotion(args);
380 return;
381 }
382
383 NotifyMotionArgs newArgs(*args);
384 newArgs.classification = mMotionClassifier->classify(newArgs);
385 mListener->notifyMotion(&newArgs);
386 }
387
notifySwitch(const NotifySwitchArgs * args)388 void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
389 // pass through
390 mListener->notifySwitch(args);
391 }
392
notifyDeviceReset(const NotifyDeviceResetArgs * args)393 void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
394 std::scoped_lock lock(mLock);
395 if (mMotionClassifier) {
396 mMotionClassifier->reset(*args);
397 }
398 // continue to next stage
399 mListener->notifyDeviceReset(args);
400 }
401
setMotionClassifier(std::unique_ptr<MotionClassifierInterface> motionClassifier)402 void InputClassifier::setMotionClassifier(
403 std::unique_ptr<MotionClassifierInterface> motionClassifier) {
404 std::scoped_lock lock(mLock);
405 mMotionClassifier = std::move(motionClassifier);
406 }
407
dump(std::string & dump)408 void InputClassifier::dump(std::string& dump) {
409 std::scoped_lock lock(mLock);
410 dump += "Input Classifier State:\n";
411
412 dump += INDENT1 "Motion Classifier:\n";
413 if (mMotionClassifier) {
414 mMotionClassifier->dump(dump);
415 } else {
416 dump += INDENT2 "<nullptr>";
417 }
418 dump += "\n";
419 }
420
~InputClassifier()421 InputClassifier::~InputClassifier() {
422 if (mInitializeMotionClassifierThread.joinable()) {
423 mInitializeMotionClassifierThread.join();
424 }
425 }
426
427 } // namespace android
428