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