• 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 "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 <server_configurable_flags/get_flags.h>
31 #include <unordered_set>
32 
33 #include <android/hardware/input/classifier/1.0/IInputClassifier.h>
34 
35 #define INDENT1 "  "
36 #define INDENT2 "    "
37 #define INDENT3 "      "
38 #define INDENT4 "        "
39 #define INDENT5 "          "
40 
41 using android::base::StringPrintf;
42 using android::hardware::hidl_bitfield;
43 using android::hardware::hidl_vec;
44 using android::hardware::Return;
45 using namespace android::hardware::input;
46 
47 namespace android {
48 
49 static constexpr bool DEBUG = false;
50 
51 // Category (=namespace) name for the input settings that are applied at boot time
52 static const char* INPUT_NATIVE_BOOT = "input_native_boot";
53 // Feature flag name for the deep press feature
54 static const char* DEEP_PRESS_ENABLED = "deep_press_enabled";
55 
56 //Max number of elements to store in mEvents.
57 static constexpr size_t MAX_EVENTS = 5;
58 
59 template<class K, class V>
getValueForKey(const std::unordered_map<K,V> & map,K key,V defaultValue)60 static V getValueForKey(const std::unordered_map<K, V>& map, K key, V defaultValue) {
61     auto it = map.find(key);
62     if (it == map.end()) {
63         return defaultValue;
64     }
65     return it->second;
66 }
67 
getMotionClassification(common::V1_0::Classification classification)68 static MotionClassification getMotionClassification(common::V1_0::Classification classification) {
69     static_assert(MotionClassification::NONE ==
70             static_cast<MotionClassification>(common::V1_0::Classification::NONE));
71     static_assert(MotionClassification::AMBIGUOUS_GESTURE ==
72             static_cast<MotionClassification>(common::V1_0::Classification::AMBIGUOUS_GESTURE));
73     static_assert(MotionClassification::DEEP_PRESS ==
74             static_cast<MotionClassification>(common::V1_0::Classification::DEEP_PRESS));
75     return static_cast<MotionClassification>(classification);
76 }
77 
isTouchEvent(const NotifyMotionArgs & args)78 static bool isTouchEvent(const NotifyMotionArgs& args) {
79     return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
80 }
81 
82 // Check if the "deep touch" feature is on.
deepPressEnabled()83 static bool deepPressEnabled() {
84     std::string flag_value = server_configurable_flags::GetServerConfigurableFlag(
85             INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true");
86     std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower);
87     if (flag_value == "1" || flag_value == "true") {
88         ALOGI("Deep press feature enabled.");
89         return true;
90     }
91     ALOGI("Deep press feature is not enabled.");
92     return false;
93 }
94 
95 
96 // --- ClassifierEvent ---
97 
ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args)98 ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
99         type(ClassifierEventType::MOTION), args(std::move(args)) { };
ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args)100 ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args) :
101         type(ClassifierEventType::DEVICE_RESET), args(std::move(args)) { };
ClassifierEvent(ClassifierEventType type,std::unique_ptr<NotifyArgs> args)102 ClassifierEvent::ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args) :
103         type(type), args(std::move(args)) { };
104 
ClassifierEvent(ClassifierEvent && other)105 ClassifierEvent::ClassifierEvent(ClassifierEvent&& other) :
106         type(other.type), args(std::move(other.args)) { };
107 
operator =(ClassifierEvent && other)108 ClassifierEvent& ClassifierEvent::operator=(ClassifierEvent&& other) {
109     type = other.type;
110     args = std::move(other.args);
111     return *this;
112 }
113 
createHalResetEvent()114 ClassifierEvent ClassifierEvent::createHalResetEvent() {
115     return ClassifierEvent(ClassifierEventType::HAL_RESET, nullptr);
116 }
117 
createExitEvent()118 ClassifierEvent ClassifierEvent::createExitEvent() {
119     return ClassifierEvent(ClassifierEventType::EXIT, nullptr);
120 }
121 
getDeviceId() const122 std::optional<int32_t> ClassifierEvent::getDeviceId() const {
123     switch (type) {
124         case ClassifierEventType::MOTION: {
125             NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(args.get());
126             return motionArgs->deviceId;
127         }
128         case ClassifierEventType::DEVICE_RESET: {
129             NotifyDeviceResetArgs* deviceResetArgs =
130                     static_cast<NotifyDeviceResetArgs*>(args.get());
131             return deviceResetArgs->deviceId;
132         }
133         case ClassifierEventType::HAL_RESET: {
134             return std::nullopt;
135         }
136         case ClassifierEventType::EXIT: {
137             return std::nullopt;
138         }
139     }
140 }
141 
142 // --- MotionClassifier ---
143 
MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient)144 MotionClassifier::MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient) :
145         mDeathRecipient(deathRecipient), mEvents(MAX_EVENTS) {
146     mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this);
147 #if defined(__linux__)
148     // Set the thread name for debugging
149     pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
150 #endif
151 }
152 
153 /**
154  * This function may block for some time to initialize the HAL, so it should only be called
155  * from the "InputClassifier HAL" thread.
156  */
init()157 bool MotionClassifier::init() {
158     ensureHalThread(__func__);
159     sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
160             classifier::V1_0::IInputClassifier::getService();
161     if (!service) {
162         // Not really an error, maybe the device does not have this HAL,
163         // but somehow the feature flag is flipped
164         ALOGI("Could not obtain InputClassifier HAL");
165         return false;
166     }
167 
168     sp<android::hardware::hidl_death_recipient> recipient = mDeathRecipient.promote();
169     if (recipient != nullptr) {
170         const bool linked = service->linkToDeath(recipient, 0 /* cookie */).withDefault(false);
171         if (!linked) {
172             ALOGE("Could not link MotionClassifier to the HAL death");
173             return false;
174         }
175     }
176 
177     // Under normal operation, we do not need to reset the HAL here. But in the case where system
178     // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
179     // have received events in the past. That means, that HAL could be in an inconsistent state
180     // once it receives events from the newly created MotionClassifier.
181     mEvents.push(ClassifierEvent::createHalResetEvent());
182 
183     {
184         std::scoped_lock lock(mLock);
185         if (mService) {
186             ALOGE("MotionClassifier::%s should only be called once", __func__);
187         }
188         mService = service;
189     }
190     return true;
191 }
192 
~MotionClassifier()193 MotionClassifier::~MotionClassifier() {
194     requestExit();
195     mHalThread.join();
196 }
197 
ensureHalThread(const char * function)198 void MotionClassifier::ensureHalThread(const char* function) {
199     if (DEBUG) {
200         if (std::this_thread::get_id() != mHalThread.get_id()) {
201             LOG_FATAL("Function %s should only be called from InputClassifier thread", function);
202         }
203     }
204 }
205 
206 /**
207  * Obtain the classification from the HAL for a given MotionEvent.
208  * Should only be called from the InputClassifier thread (mHalThread).
209  * Should not be called from the thread that notifyMotion runs on.
210  *
211  * There is no way to provide a timeout for a HAL call. So if the HAL takes too long
212  * to return a classification, this would directly impact the touch latency.
213  * To remove any possibility of negatively affecting the touch latency, the HAL
214  * is called from a dedicated thread.
215  */
callInputClassifierHal()216 void MotionClassifier::callInputClassifierHal() {
217     ensureHalThread(__func__);
218     const bool initialized = init();
219     if (!initialized) {
220         // MotionClassifier no longer useful.
221         // Deliver death notification from a separate thread
222         // because ~MotionClassifier may be invoked, which calls mHalThread.join()
223         std::thread([deathRecipient = mDeathRecipient](){
224                 sp<android::hardware::hidl_death_recipient> recipient = deathRecipient.promote();
225                 if (recipient != nullptr) {
226                     recipient->serviceDied(0 /*cookie*/, nullptr);
227                 }
228         }).detach();
229         return;
230     }
231     // From this point on, mService is guaranteed to be non-null.
232 
233     while (true) {
234         ClassifierEvent event = mEvents.pop();
235         bool halResponseOk = true;
236         switch (event.type) {
237             case ClassifierEventType::MOTION: {
238                 NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get());
239                 common::V1_0::MotionEvent motionEvent =
240                         notifyMotionArgsToHalMotionEvent(*motionArgs);
241                 Return<common::V1_0::Classification> response = mService->classify(motionEvent);
242                 halResponseOk = response.isOk();
243                 if (halResponseOk) {
244                     common::V1_0::Classification halClassification = response;
245                     updateClassification(motionArgs->deviceId, motionArgs->eventTime,
246                             getMotionClassification(halClassification));
247                 }
248                 break;
249             }
250             case ClassifierEventType::DEVICE_RESET: {
251                 const int32_t deviceId = *(event.getDeviceId());
252                 halResponseOk = mService->resetDevice(deviceId).isOk();
253                 setClassification(deviceId, MotionClassification::NONE);
254                 break;
255             }
256             case ClassifierEventType::HAL_RESET: {
257                 halResponseOk = mService->reset().isOk();
258                 clearClassifications();
259                 break;
260             }
261             case ClassifierEventType::EXIT: {
262                 clearClassifications();
263                 return;
264             }
265         }
266         if (!halResponseOk) {
267             ALOGE("Error communicating with InputClassifier HAL. "
268                     "Exiting MotionClassifier HAL thread");
269             clearClassifications();
270             return;
271         }
272     }
273 }
274 
enqueueEvent(ClassifierEvent && event)275 void MotionClassifier::enqueueEvent(ClassifierEvent&& event) {
276     bool eventAdded = mEvents.push(std::move(event));
277     if (!eventAdded) {
278         // If the queue is full, suspect the HAL is slow in processing the events.
279         ALOGE("Dropped event with eventTime %" PRId64, event.args->eventTime);
280         reset();
281     }
282 }
283 
requestExit()284 void MotionClassifier::requestExit() {
285     reset();
286     mEvents.push(ClassifierEvent::createExitEvent());
287 }
288 
updateClassification(int32_t deviceId,nsecs_t eventTime,MotionClassification classification)289 void MotionClassifier::updateClassification(int32_t deviceId, nsecs_t eventTime,
290         MotionClassification classification) {
291     std::scoped_lock lock(mLock);
292     const nsecs_t lastDownTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
293     if (eventTime < lastDownTime) {
294         // HAL just finished processing an event that belonged to an earlier gesture,
295         // but new gesture is already in progress. Drop this classification.
296         ALOGW("Received late classification. Late by at least %" PRId64 " ms.",
297                 nanoseconds_to_milliseconds(lastDownTime - eventTime));
298         return;
299     }
300     mClassifications[deviceId] = classification;
301 }
302 
setClassification(int32_t deviceId,MotionClassification classification)303 void MotionClassifier::setClassification(int32_t deviceId, MotionClassification classification) {
304     std::scoped_lock lock(mLock);
305     mClassifications[deviceId] = classification;
306 }
307 
clearClassifications()308 void MotionClassifier::clearClassifications() {
309     std::scoped_lock lock(mLock);
310     mClassifications.clear();
311 }
312 
getClassification(int32_t deviceId)313 MotionClassification MotionClassifier::getClassification(int32_t deviceId) {
314     std::scoped_lock lock(mLock);
315     return getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
316 }
317 
updateLastDownTime(int32_t deviceId,nsecs_t downTime)318 void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) {
319     std::scoped_lock lock(mLock);
320     mLastDownTimes[deviceId] = downTime;
321     mClassifications[deviceId] = MotionClassification::NONE;
322 }
323 
classify(const NotifyMotionArgs & args)324 MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
325     if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
326         updateLastDownTime(args.deviceId, args.downTime);
327     }
328 
329     ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args));
330     enqueueEvent(std::move(event));
331     return getClassification(args.deviceId);
332 }
333 
reset()334 void MotionClassifier::reset() {
335     mEvents.clear();
336     mEvents.push(ClassifierEvent::createHalResetEvent());
337 }
338 
339 /**
340  * Per-device reset. Clear the outstanding events that are going to be sent to HAL.
341  * Request InputClassifier thread to call resetDevice for this particular device.
342  */
reset(const NotifyDeviceResetArgs & args)343 void MotionClassifier::reset(const NotifyDeviceResetArgs& args) {
344     int32_t deviceId = args.deviceId;
345     // Clear the pending events right away, to avoid unnecessary work done by the HAL.
346     mEvents.erase([deviceId](const ClassifierEvent& event) {
347             std::optional<int32_t> eventDeviceId = event.getDeviceId();
348             return eventDeviceId && (*eventDeviceId == deviceId);
349     });
350     enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args));
351 }
352 
getServiceStatus()353 const char* MotionClassifier::getServiceStatus() REQUIRES(mLock) {
354     if (!mService) {
355         return "null";
356     }
357     if (mService->ping().isOk()) {
358         return "running";
359     }
360     return "not responding";
361 }
362 
dump(std::string & dump)363 void MotionClassifier::dump(std::string& dump) {
364     std::scoped_lock lock(mLock);
365     dump += StringPrintf(INDENT2 "mService status: %s\n", getServiceStatus());
366     dump += StringPrintf(INDENT2 "mEvents: %zu element(s) (max=%zu)\n",
367             mEvents.size(), MAX_EVENTS);
368     dump += INDENT2 "mClassifications, mLastDownTimes:\n";
369     dump += INDENT3 "Device Id\tClassification\tLast down time";
370     // Combine mClassifications and mLastDownTimes into a single table.
371     // Create a superset of device ids.
372     std::unordered_set<int32_t> deviceIds;
373     std::for_each(mClassifications.begin(), mClassifications.end(),
374             [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
375     std::for_each(mLastDownTimes.begin(), mLastDownTimes.end(),
376             [&deviceIds](auto pair){ deviceIds.insert(pair.first); });
377     for(int32_t deviceId : deviceIds) {
378         const MotionClassification classification =
379                 getValueForKey(mClassifications, deviceId, MotionClassification::NONE);
380         const nsecs_t downTime = getValueForKey(mLastDownTimes, deviceId, static_cast<nsecs_t>(0));
381         dump += StringPrintf("\n" INDENT4 "%" PRId32 "\t%s\t%" PRId64,
382                 deviceId, motionClassificationToString(classification), downTime);
383     }
384 }
385 
386 
387 // --- InputClassifier ---
388 
InputClassifier(const sp<InputListenerInterface> & listener)389 InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) :
390         mListener(listener) {
391     // The rest of the initialization is done in onFirstRef, because we need to obtain
392     // an sp to 'this' in order to register for HAL death notifications
393 }
394 
onFirstRef()395 void InputClassifier::onFirstRef() {
396     if (!deepPressEnabled()) {
397         // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work.
398         // When MotionClassifier is null, InputClassifier will forward all events
399         // to the next InputListener, unmodified.
400         return;
401     }
402     std::scoped_lock lock(mLock);
403     mMotionClassifier = std::make_unique<MotionClassifier>(this);
404 }
405 
notifyConfigurationChanged(const NotifyConfigurationChangedArgs * args)406 void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
407     // pass through
408     mListener->notifyConfigurationChanged(args);
409 }
410 
notifyKey(const NotifyKeyArgs * args)411 void InputClassifier::notifyKey(const NotifyKeyArgs* args) {
412     // pass through
413     mListener->notifyKey(args);
414 }
415 
notifyMotion(const NotifyMotionArgs * args)416 void InputClassifier::notifyMotion(const NotifyMotionArgs* args) {
417     std::scoped_lock lock(mLock);
418     // MotionClassifier is only used for touch events, for now
419     const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args);
420     if (!sendToMotionClassifier) {
421         mListener->notifyMotion(args);
422         return;
423     }
424 
425     NotifyMotionArgs newArgs(*args);
426     newArgs.classification = mMotionClassifier->classify(newArgs);
427     mListener->notifyMotion(&newArgs);
428 }
429 
notifySwitch(const NotifySwitchArgs * args)430 void InputClassifier::notifySwitch(const NotifySwitchArgs* args) {
431     // pass through
432     mListener->notifySwitch(args);
433 }
434 
notifyDeviceReset(const NotifyDeviceResetArgs * args)435 void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
436     std::scoped_lock lock(mLock);
437     if (mMotionClassifier) {
438         mMotionClassifier->reset(*args);
439     }
440     // continue to next stage
441     mListener->notifyDeviceReset(args);
442 }
443 
serviceDied(uint64_t,const wp<android::hidl::base::V1_0::IBase> & who)444 void InputClassifier::serviceDied(uint64_t /*cookie*/,
445         const wp<android::hidl::base::V1_0::IBase>& who) {
446     std::scoped_lock lock(mLock);
447     ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null");
448     mMotionClassifier = nullptr;
449     sp<android::hidl::base::V1_0::IBase> service = who.promote();
450     if (service) {
451         service->unlinkToDeath(this);
452     }
453 }
454 
dump(std::string & dump)455 void InputClassifier::dump(std::string& dump) {
456     std::scoped_lock lock(mLock);
457     dump += "Input Classifier State:\n";
458 
459     dump += INDENT1 "Motion Classifier:\n";
460     if (mMotionClassifier) {
461         mMotionClassifier->dump(dump);
462     } else {
463         dump += INDENT2 "<nullptr>";
464     }
465     dump += "\n";
466 }
467 
468 } // namespace android
469