• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "InputCommonConverter.h"
21 
22 #include <android-base/stringprintf.h>
23 #include <android/binder_manager.h>
24 #include <android/binder_process.h>
25 #include <inttypes.h>
26 #include <log/log.h>
27 #include <algorithm>
28 #include <cmath>
29 #if defined(__linux__)
30     #include <pthread.h>
31 #endif
32 #include <unordered_set>
33 
34 #define INDENT1 "  "
35 #define INDENT2 "    "
36 #define INDENT3 "      "
37 #define INDENT4 "        "
38 #define INDENT5 "          "
39 
40 using android::base::StringPrintf;
41 using namespace std::chrono_literals;
42 using namespace ::aidl::android::hardware::input;
43 using aidl::android::hardware::input::processor::IInputProcessor;
44 
45 namespace android {
46 
47 //Max number of elements to store in mEvents.
48 static constexpr size_t MAX_EVENTS = 5;
49 
50 template<class K, class V>
getValueForKey(const std::unordered_map<K,V> & map,K key,V defaultValue)51 static V getValueForKey(const std::unordered_map<K, V>& map, K key, V defaultValue) {
52     auto it = map.find(key);
53     if (it == map.end()) {
54         return defaultValue;
55     }
56     return it->second;
57 }
58 
getMotionClassification(common::Classification classification)59 static MotionClassification getMotionClassification(common::Classification classification) {
60     static_assert(MotionClassification::NONE ==
61                   static_cast<MotionClassification>(common::Classification::NONE));
62     static_assert(MotionClassification::AMBIGUOUS_GESTURE ==
63                   static_cast<MotionClassification>(common::Classification::AMBIGUOUS_GESTURE));
64     static_assert(MotionClassification::DEEP_PRESS ==
65                   static_cast<MotionClassification>(common::Classification::DEEP_PRESS));
66     return static_cast<MotionClassification>(classification);
67 }
68 
isTouchEvent(const NotifyMotionArgs & args)69 static bool isTouchEvent(const NotifyMotionArgs& args) {
70     return isFromSource(args.source, AINPUT_SOURCE_TOUCHPAD) ||
71             isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN);
72 }
73 
setCurrentThreadName(const char * name)74 static void setCurrentThreadName(const char* name) {
75 #if defined(__linux__)
76     // Set the thread name for debugging
77     pthread_setname_np(pthread_self(), name);
78 #else
79     (void*)(name); // prevent unused variable warning
80 #endif
81 }
82 
getService()83 static std::shared_ptr<IInputProcessor> getService() {
84     const std::string aidl_instance_name = std::string(IInputProcessor::descriptor) + "/default";
85 
86     if (!AServiceManager_isDeclared(aidl_instance_name.c_str())) {
87         ALOGI("HAL %s is not declared", aidl_instance_name.c_str());
88         return nullptr;
89     }
90 
91     ndk::SpAIBinder binder(AServiceManager_waitForService(aidl_instance_name.c_str()));
92     return IInputProcessor::fromBinder(binder);
93 }
94 
95 // Temporarily releases a held mutex for the lifetime of the instance.
96 // Named to match std::scoped_lock
97 class scoped_unlock {
98 public:
scoped_unlock(std::mutex & mutex)99     explicit scoped_unlock(std::mutex& mutex) : mMutex(mutex) { mMutex.unlock(); }
~scoped_unlock()100     ~scoped_unlock() { mMutex.lock(); }
101 
102 private:
103     std::mutex& mMutex;
104 };
105 
106 // --- ScopedDeathRecipient ---
ScopedDeathRecipient(AIBinder_DeathRecipient_onBinderDied onBinderDied,void * cookie)107 ScopedDeathRecipient::ScopedDeathRecipient(AIBinder_DeathRecipient_onBinderDied onBinderDied,
108                                            void* cookie)
109       : mCookie(cookie) {
110     mRecipient = AIBinder_DeathRecipient_new(onBinderDied);
111 }
112 
linkToDeath(AIBinder * binder)113 void ScopedDeathRecipient::linkToDeath(AIBinder* binder) {
114     binder_status_t linked = AIBinder_linkToDeath(binder, mRecipient, mCookie);
115     if (linked != STATUS_OK) {
116         ALOGE("Could not link death recipient to the HAL death");
117     }
118 }
119 
~ScopedDeathRecipient()120 ScopedDeathRecipient::~ScopedDeathRecipient() {
121     AIBinder_DeathRecipient_delete(mRecipient);
122 }
123 
124 // --- ClassifierEvent ---
125 
ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args)126 ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
127         type(ClassifierEventType::MOTION), args(std::move(args)) { };
ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args)128 ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args) :
129         type(ClassifierEventType::DEVICE_RESET), args(std::move(args)) { };
ClassifierEvent(ClassifierEventType type,std::unique_ptr<NotifyArgs> args)130 ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args) :
131         type(type), args(std::move(args)) { };
132 
ClassifierEvent(ClassifierEvent && other)133 ClassifierEvent::ClassifierEvent(ClassifierEvent&& other) :
134         type(other.type), args(std::move(other.args)) { };
135 
operator =(ClassifierEvent && other)136 ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) {
137     type = other.type;
138     args = std::move(other.args);
139     return *this;
140 }
141 
createHalResetEvent()142 ClassifierEvent ClassifierEvent::createHalResetEvent() {
143     return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr);
144 }
145 
createExitEvent()146 ClassifierEvent ClassifierEvent::createExitEvent() {
147     return ClassifierEvent(ClassifierEventType::EXIT, nullptr);
148 }
149 
getDeviceId() const150 std::optional<int32_t> ClassifierEvent::getDeviceId() const {
151     switch (type) {
152         case ClassifierEventType::MOTION: {
153             NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(args.get());
154             return motionArgs->deviceId;
155         }
156         case ClassifierEventType::DEVICE_RESET: {
157             NotifyDeviceResetArgs* deviceResetArgs =
158                     static_cast<NotifyDeviceResetArgs*>(args.get());
159             return deviceResetArgs->deviceId;
160         }
161         case ClassifierEventType::HAL_RESET: {
162             return std::nullopt;
163         }
164         case ClassifierEventType::EXIT: {
165             return std::nullopt;
166         }
167     }
168 }
169 
170 // --- MotionClassifier ---
171 
MotionClassifier(std::shared_ptr<IInputProcessor> service)172 MotionClassifier::MotionClassifier(std::shared_ptr<IInputProcessor> service)
173       : mEvents(MAX_EVENTS), mService(std::move(service)) {
174     // Under normal operation, we do not need to reset the HAL here. But in the case where system
175     // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
176     // have received events in the past. That means, that HAL could be in an inconsistent state
177     // once it receives events from the newly created MotionClassifier.
178     mEvents.push(ClassifierEvent::createHalResetEvent());
179 
180     mHalThread = std::thread(&MotionClassifier::processEvents, this);
181 #if defined(__linux__)
182     // Set the thread name for debugging
183     pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
184 #endif
185 }
186 
create(std::shared_ptr<IInputProcessor> service)187 std::unique_ptr<MotionClassifierInterface> MotionClassifier::create(
188         std::shared_ptr<IInputProcessor> service) {
189     LOG_ALWAYS_FATAL_IF(service == nullptr);
190     // Using 'new' to access a non-public constructor
191     return std::unique_ptr<MotionClassifier>(new MotionClassifier(std::move(service)));
192 }
193 
~MotionClassifier()194 MotionClassifier::~MotionClassifier() {
195     requestExit();
196     mHalThread.join();
197 }
198 
199 /**
200  * Obtain the classification from the HAL for a given MotionEvent.
201  * Should only be called from the InputClassifier thread (mHalThread).
202  * Should not be called from the thread that notifyMotion runs on.
203  *
204  * There is no way to provide a timeout for a HAL call. So if the HAL takes too long
205  * to return a classification, this would directly impact the touch latency.
206  * To remove any possibility of negatively affecting the touch latency, the HAL
207  * is called from a dedicated thread.
208  */
processEvents()209 void MotionClassifier::processEvents() {
210     while (true) {
211         ClassifierEvent event = mEvents.pop();
212         bool halResponseOk = true;
213         switch (event.type) {
214             case ClassifierEventType::MOTION: {
215                 NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
216                 common::MotionEvent motionEvent = notifyMotionArgsToHalMotionEvent(*motionArgs);
217                 common::Classification classification;
218                 ndk::ScopedAStatus response = mService->classify(motionEvent, &classification);
219                 if (response.isOk()) {
220                     updateClassification(motionArgs->deviceId, motionArgs->eventTime,
221                                          getMotionClassification(classification));
222                 }
223                 break;
224             }
225             case ClassifierEventType::DEVICE_RESET: {
226                 const int32_t deviceId = *(event.getDeviceId());
227                 halResponseOk = mService->resetDevice(deviceId).isOk();
228                 clearDeviceState(deviceId);
229                 break;
230             }
231             case ClassifierEventType::HAL_RESET: {
232                 halResponseOk = mService->reset().isOk();
233                 clearClassifications();
234                 break;
235             }
236             case ClassifierEventType::EXIT: {
237                 clearClassifications();
238                 return;
239             }
240         }
241         if (!halResponseOk) {
242             ALOGE("Error communicating with InputClassifier HAL. "
243                     "Exiting MotionClassifier HAL thread");
244             clearClassifications();
245             return;
246         }
247     }
248 }
249 
enqueueEvent(ClassifierEvent && event)250 void MotionClassifier::enqueueEvent(ClassifierEvent&& event) {
251     bool eventAdded = mEvents.push(std::move(event));
252     if (!eventAdded) {
253         // If the queue is full, suspect the HAL is slow in processing the events.
254         ALOGE("Could not add the event to the queue. Resetting");
255         reset();
256     }
257 }
258 
requestExit()259 void MotionClassifier::requestExit() {
260     reset();
261     mEvents.push(ClassifierEvent::createExitEvent());
262 }
263 
updateClassification(int32_t deviceId,nsecs_t eventTime,MotionClassification classification)264 void MotionClassifier::updateClassification(int32_t deviceId, nsecs_t eventTime,
265         MotionClassification classification) {
266     std::scoped_lock lock(mLock);
267     const nsecs_t lastDownTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
268     if (eventTime < lastDownTime) {
269         // HAL just finished processing an event that belonged to an earlier gesture,
270         // but new gesture is already in progress. Drop this classification.
271         ALOGW("Received late classification. Late by at least %" PRId64 " ms.",
272                 nanoseconds_to_milliseconds(lastDownTime - eventTime));
273         return;
274     }
275     mClassifications[deviceId] = classification;
276 }
277 
setClassification(int32_t deviceId,MotionClassification classification)278 void MotionClassifier::setClassification(int32_t deviceId, MotionClassification classification) {
279     std::scoped_lock lock(mLock);
280     mClassifications[deviceId] = classification;
281 }
282 
clearClassifications()283 void MotionClassifier::clearClassifications() {
284     std::scoped_lock lock(mLock);
285     mClassifications.clear();
286 }
287 
getClassification(int32_t deviceId)288 MotionClassification MotionClassifier::getClassification(int32_t deviceId) {
289     std::scoped_lock lock(mLock);
290     return getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
291 }
292 
updateLastDownTime(int32_t deviceId,nsecs_t downTime)293 void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) {
294     std::scoped_lock lock(mLock);
295     mLastDownTimes[deviceId] = downTime;
296     mClassifications[deviceId] = MotionClassification::NONE;
297 }
298 
clearDeviceState(int32_t deviceId)299 void MotionClassifier::clearDeviceState(int32_t deviceId) {
300     std::scoped_lock lock(mLock);
301     mClassifications.erase(deviceId);
302     mLastDownTimes.erase(deviceId);
303 }
304 
classify(const NotifyMotionArgs & args)305 MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
306     if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
307         updateLastDownTime(args.deviceId, args.downTime);
308     }
309 
310     ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args));
311     enqueueEvent(std::move(event));
312     return getClassification(args.deviceId);
313 }
314 
reset()315 void MotionClassifier::reset() {
316     mEvents.clear();
317     mEvents.push(ClassifierEvent::createHalResetEvent());
318 }
319 
320 /**
321  * Per-device reset. Clear the outstanding events that are going to be sent to HAL.
322  * Request InputClassifier thread to call resetDevice for this particular device.
323  */
reset(const NotifyDeviceResetArgs & args)324 void MotionClassifier::reset(const NotifyDeviceResetArgs& args) {
325     int32_t deviceId = args.deviceId;
326     // Clear the pending events right away, to avoid unnecessary work done by the HAL.
327     mEvents.erase([deviceId](const ClassifierEvent& event) {
328             std::optional<int32_t> eventDeviceId = event.getDeviceId();
329             return eventDeviceId && (*eventDeviceId == deviceId);
330     });
331     enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args));
332 }
333 
getServiceStatus()334 const char* MotionClassifier::getServiceStatus() REQUIRES(mLock) {
335     if (!mService) {
336         return "null";
337     }
338 
339     if (AIBinder_ping(mService->asBinder().get()) == STATUS_OK) {
340         return "running";
341     }
342     return "not responding";
343 }
344 
dump(std::string & dump)345 void MotionClassifier::dump(std::string& dump) {
346     std::scoped_lock lock(mLock);
347     dump += StringPrintf(INDENT2 "mService status: %s\n", getServiceStatus());
348     dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n",
349             mEvents.size(), MAX_EVENTS);
350     dump += INDENT2 "mClassifications, mLastDownTimes:\n";
351     dump += INDENT3 "Device Id\tClassification\tLast down time";
352     // Combine mClassifications and mLastDownTimes into a single table.
353     // Create a superset of device ids.
354     std::unordered_set<int32_t> deviceIds;
355     std::for_each(mClassifications.begin(), mClassifications.end(),
356             [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
357     std::for_each(mLastDownTimes.begin(), mLastDownTimes.end(),
358             [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
359     for(int32_t deviceId : deviceIds) {
360         const MotionClassification classification =
361                 getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
362         const nsecs_t downTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
363         dump += StringPrintf("\n" INDENT4 "%" PRId32 "\t%s\t%" PRId64,
364                 deviceId, motionClassificationToString(classification), downTime);
365     }
366 }
367 
368 // --- InputClassifier ---
369 
InputClassifier(InputListenerInterface & listener)370 InputClassifier::InputClassifier(InputListenerInterface& listener) : mQueuedListener(listener) {}
371 
onBinderDied(void * cookie)372 void InputClassifier::onBinderDied(void* cookie) {
373     InputClassifier* classifier = static_cast<InputClassifier*>(cookie);
374     if (classifier == nullptr) {
375         LOG_ALWAYS_FATAL("Cookie is not valid");
376         return;
377     }
378     classifier->setMotionClassifierEnabled(false);
379 }
380 
setMotionClassifierEnabled(bool enabled)381 void InputClassifier::setMotionClassifierEnabled(bool enabled) {
382     std::scoped_lock lock(mLock);
383     if (enabled) {
384         ALOGI("Enabling motion classifier");
385         if (mInitializeMotionClassifier.valid()) {
386             scoped_unlock unlock(mLock);
387             std::future_status status = mInitializeMotionClassifier.wait_for(5s);
388             if (status != std::future_status::ready) {
389                 /**
390                  * We don't have a better option here than to crash. We can't stop the thread,
391                  * and we can't continue because 'mInitializeMotionClassifier' will block in its
392                  * destructor.
393                  */
394                 LOG_ALWAYS_FATAL("The thread to load IInputClassifier is stuck!");
395             }
396         }
397         mInitializeMotionClassifier = std::async(std::launch::async, [this] {
398             setCurrentThreadName("Create MotionClassifier");
399             std::shared_ptr<IInputProcessor> service = getService();
400             if (service == nullptr) {
401                 // Keep the MotionClassifier null, no service was found
402                 return;
403             }
404             { // acquire lock
405                 std::scoped_lock threadLock(mLock);
406                 mHalDeathRecipient =
407                         std::make_unique<ScopedDeathRecipient>(onBinderDied, this /*cookie*/);
408                 mHalDeathRecipient->linkToDeath(service->asBinder().get());
409                 setMotionClassifierLocked(MotionClassifier::create(std::move(service)));
410             } // release lock
411         });
412     } else {
413         ALOGI("Disabling motion classifier");
414         setMotionClassifierLocked(nullptr);
415     }
416 }
417 
notifyConfigurationChanged(const NotifyConfigurationChangedArgs * args)418 void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
419     // pass through
420     mQueuedListener.notifyConfigurationChanged(args);
421     mQueuedListener.flush();
422 }
423 
notifyKey(const NotifyKeyArgs * args)424 void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
425     // pass through
426     mQueuedListener.notifyKey(args);
427     mQueuedListener.flush();
428 }
429 
notifyMotion(const NotifyMotionArgs * args)430 void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
431     { // acquire lock
432         std::scoped_lock lock(mLock);
433         // MotionClassifier is only used for touch events, for now
434         const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
435         if (!sendToMotionClassifier) {
436             mQueuedListener.notifyMotion(args);
437         } else {
438             NotifyMotionArgs newArgs(*args);
439             newArgs.classification = mMotionClassifier->classify(newArgs);
440             mQueuedListener.notifyMotion(&newArgs);
441         }
442     } // release lock
443     mQueuedListener.flush();
444 }
445 
notifySensor(const NotifySensorArgs * args)446 void InputClassifier::notifySensor(const NotifySensorArgs* args) {
447     // pass through
448     mQueuedListener.notifySensor(args);
449     mQueuedListener.flush();
450 }
451 
notifyVibratorState(const NotifyVibratorStateArgs * args)452 void InputClassifier::notifyVibratorState(const NotifyVibratorStateArgs* args) {
453     // pass through
454     mQueuedListener.notifyVibratorState(args);
455     mQueuedListener.flush();
456 }
457 
notifySwitch(const NotifySwitchArgs * args)458 void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
459     // pass through
460     mQueuedListener.notifySwitch(args);
461     mQueuedListener.flush();
462 }
463 
notifyDeviceReset(const NotifyDeviceResetArgs * args)464 void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
465     { // acquire lock
466         std::scoped_lock lock(mLock);
467         if (mMotionClassifier) {
468             mMotionClassifier->reset(*args);
469         }
470     } // release lock
471 
472     // continue to next stage
473     mQueuedListener.notifyDeviceReset(args);
474     mQueuedListener.flush();
475 }
476 
notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs * args)477 void InputClassifier::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) {
478     // pass through
479     mQueuedListener.notifyPointerCaptureChanged(args);
480     mQueuedListener.flush();
481 }
482 
setMotionClassifierLocked(std::unique_ptr<MotionClassifierInterface> motionClassifier)483 void InputClassifier::setMotionClassifierLocked(
484         std::unique_ptr<MotionClassifierInterface> motionClassifier) REQUIRES(mLock) {
485     if (motionClassifier == nullptr) {
486         // Destroy the ScopedDeathRecipient object, which will cause it to unlinkToDeath.
487         // We can't call 'unlink' here because we don't have the binder handle.
488         mHalDeathRecipient = nullptr;
489     }
490     mMotionClassifier = std::move(motionClassifier);
491 }
492 
dump(std::string & dump)493 void InputClassifier::dump(std::string& dump) {
494     std::scoped_lock lock(mLock);
495     dump += "Input Classifier State:\n";
496     dump += INDENT1 "Motion Classifier:\n";
497     if (mMotionClassifier) {
498         mMotionClassifier->dump(dump);
499     } else {
500         dump += INDENT2 "<nullptr>";
501     }
502     dump += "\n";
503 }
504 
monitor()505 void InputClassifier::monitor() {
506     std::scoped_lock lock(mLock);
507 }
508 
~InputClassifier()509 InputClassifier::~InputClassifier() {
510 }
511 
512 } // namespace android
513