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