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