• 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 #ifndef _UI_INPUT_CLASSIFIER_H
18 #define _UI_INPUT_CLASSIFIER_H
19 
20 #include <android-base/thread_annotations.h>
21 #include <future>
22 #include <thread>
23 #include <unordered_map>
24 
25 #include <aidl/android/hardware/input/processor/IInputProcessor.h>
26 #include "BlockingQueue.h"
27 #include "InputListener.h"
28 namespace android {
29 
30 enum class ClassifierEventType : uint8_t {
31     MOTION = 0,
32     DEVICE_RESET = 1,
33     HAL_RESET = 2,
34     EXIT = 3,
35 };
36 
37 struct ClassifierEvent {
38     ClassifierEventType type;
39     std::unique_ptr<NotifyArgs> args;
40 
41     ClassifierEvent(ClassifierEventType type, std::unique_ptr<NotifyArgs> args);
42     ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args);
43     ClassifierEvent(std::unique_ptr<NotifyDeviceResetArgs> args);
44     ClassifierEvent(ClassifierEvent&& other);
45     ClassifierEvent& operator=(ClassifierEvent&& other);
46 
47     // Convenience function to create a HAL_RESET event
48     static ClassifierEvent createHalResetEvent();
49     // Convenience function to create an EXIT event
50     static ClassifierEvent createExitEvent();
51 
52     std::optional<int32_t> getDeviceId() const;
53 };
54 
55 // --- Interfaces ---
56 
57 /**
58  * Interface for adding a MotionClassification to NotifyMotionArgs.
59  *
60  * To implement, override the classify function.
61  */
62 class MotionClassifierInterface {
63 public:
MotionClassifierInterface()64     MotionClassifierInterface() { }
~MotionClassifierInterface()65     virtual ~MotionClassifierInterface() { }
66     /**
67      * Based on the motion event described by NotifyMotionArgs,
68      * provide a MotionClassification for the current gesture.
69      */
70     virtual MotionClassification classify(const NotifyMotionArgs& args) = 0;
71     /**
72      * Reset all internal HAL state.
73      */
74     virtual void reset() = 0;
75     /**
76      * Reset HAL state for a specific device.
77      */
78     virtual void reset(const NotifyDeviceResetArgs& args) = 0;
79 
80     /**
81      * Dump the state of the motion classifier
82      */
83     virtual void dump(std::string& dump) = 0;
84 };
85 
86 /**
87  * Base interface for an InputListener stage.
88  * Provides classification to events.
89  */
90 class InputClassifierInterface : public InputListenerInterface {
91 public:
92     virtual void setMotionClassifierEnabled(bool enabled) = 0;
93     /**
94      * Dump the state of the input classifier.
95      * This method may be called on any thread (usually by the input manager).
96      */
97     virtual void dump(std::string& dump) = 0;
98 
99     /* Called by the heatbeat to ensures that the classifier has not deadlocked. */
100     virtual void monitor() = 0;
101 
InputClassifierInterface()102     InputClassifierInterface() { }
~InputClassifierInterface()103     virtual ~InputClassifierInterface() { }
104 };
105 
106 // --- Implementations ---
107 
108 class ScopedDeathRecipient {
109 public:
110     explicit ScopedDeathRecipient(AIBinder_DeathRecipient_onBinderDied onBinderDied, void* cookie);
111     ScopedDeathRecipient(const ScopedDeathRecipient&) = delete;
112     ScopedDeathRecipient& operator=(ScopedDeathRecipient const&) = delete;
113     void linkToDeath(AIBinder* binder);
114     ~ScopedDeathRecipient();
115 
116 private:
117     AIBinder_DeathRecipient* mRecipient;
118     void* mCookie;
119 };
120 
121 /**
122  * Implementation of MotionClassifierInterface that calls the InputClassifier HAL
123  * in order to determine the classification for the current gesture.
124  *
125  * The InputClassifier HAL may keep track of the entire gesture in order to determine
126  * the classification, and may be hardware-specific. It may use the data in
127  * NotifyMotionArgs::videoFrames field to drive the classification decisions.
128  * The HAL is called from a separate thread.
129  */
130 class MotionClassifier final : public MotionClassifierInterface {
131 public:
132     /*
133      * Create an instance of MotionClassifier.
134      * The death recipient, if provided, will be subscribed to the HAL death.
135      * The death recipient could be used to destroy MotionClassifier.
136      *
137      * This function should be called asynchronously, because getService takes a long time.
138      */
139     static std::unique_ptr<MotionClassifierInterface> create(
140             std::shared_ptr<aidl::android::hardware::input::processor::IInputProcessor> service);
141 
142     ~MotionClassifier();
143 
144     /**
145      * Classifies events asynchronously; that is, it doesn't block events on a classification,
146      * but instead sends them over to the classifier HAL. After a classification of a specific
147      * event is determined, MotionClassifier then marks the next event in the stream with this
148      * classification.
149      *
150      * Therefore, it is acceptable to have the classifications be delayed by 1-2 events
151      * in a particular gesture.
152      */
153     virtual MotionClassification classify(const NotifyMotionArgs& args) override;
154     virtual void reset() override;
155     virtual void reset(const NotifyDeviceResetArgs& args) override;
156 
157     virtual void dump(std::string& dump) override;
158 
159 private:
160     friend class MotionClassifierTest; // to create MotionClassifier with a test HAL implementation
161     explicit MotionClassifier(
162             std::shared_ptr<aidl::android::hardware::input::processor::IInputProcessor> service);
163 
164     // The events that need to be sent to the HAL.
165     BlockingQueue<ClassifierEvent> mEvents;
166     /**
167      * Add an event to the queue mEvents.
168      */
169     void enqueueEvent(ClassifierEvent&& event);
170     /**
171      * Thread that will communicate with InputClassifier HAL.
172      * This should be the only thread that communicates with InputClassifier HAL,
173      * because this thread is allowed to block on the HAL calls.
174      */
175     std::thread mHalThread;
176     /**
177      * Process events and call the InputClassifier HAL
178      */
179     void processEvents();
180     /**
181      * Access to the InputProcessor HAL. May be null if init() hasn't completed yet.
182      * When init() successfully completes, mService is guaranteed to remain non-null and to not
183      * change its value until MotionClassifier is destroyed.
184      * This variable is *not* guarded by mLock in the InputClassifier thread, because
185      * that thread knows exactly when this variable is initialized.
186      * When accessed in any other thread, mService is checked for nullness with a lock.
187      */
188     std::shared_ptr<aidl::android::hardware::input::processor::IInputProcessor> mService;
189     std::mutex mLock;
190     /**
191      * Per-device input classifications. Should only be accessed using the
192      * getClassification / setClassification methods.
193      */
194     std::unordered_map<int32_t /*deviceId*/, MotionClassification>
195             mClassifications GUARDED_BY(mLock);
196     /**
197      * Set the current classification for a given device.
198      */
199     void setClassification(int32_t deviceId, MotionClassification classification);
200     /**
201      * Get the current classification for a given device.
202      */
203     MotionClassification getClassification(int32_t deviceId);
204     void updateClassification(int32_t deviceId, nsecs_t eventTime,
205             MotionClassification classification);
206     /**
207      * Clear all current classifications
208      */
209     void clearClassifications();
210     /**
211      * Per-device times when the last ACTION_DOWN was received.
212      * Used to reject late classifications that do not belong to the current gesture.
213      *
214      * Accessed indirectly by both InputClassifier thread and the thread that receives notifyMotion.
215      */
216     std::unordered_map<int32_t /*deviceId*/, nsecs_t /*downTime*/> mLastDownTimes GUARDED_BY(mLock);
217 
218     void updateLastDownTime(int32_t deviceId, nsecs_t downTime);
219 
220     void clearDeviceState(int32_t deviceId);
221 
222     /**
223      * Exit the InputClassifier HAL thread.
224      * Useful for tests to ensure proper cleanup.
225      */
226     void requestExit();
227     /**
228      * Return string status of mService
229      */
230     const char* getServiceStatus() REQUIRES(mLock);
231 };
232 
233 /**
234  * Implementation of the InputClassifierInterface.
235  * Represents a separate stage of input processing. All of the input events go through this stage.
236  * Acts as a passthrough for all input events except for motion events.
237  * The events of motion type are sent to MotionClassifier.
238  */
239 class InputClassifier : public InputClassifierInterface {
240 public:
241     explicit InputClassifier(InputListenerInterface& listener);
242 
243     void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
244     void notifyKey(const NotifyKeyArgs* args) override;
245     void notifyMotion(const NotifyMotionArgs* args) override;
246     void notifySwitch(const NotifySwitchArgs* args) override;
247     void notifySensor(const NotifySensorArgs* args) override;
248     void notifyVibratorState(const NotifyVibratorStateArgs* args) override;
249     void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
250     void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override;
251 
252     void dump(std::string& dump) override;
253     void monitor() override;
254 
255     ~InputClassifier();
256 
257     // Called from InputManager
258     void setMotionClassifierEnabled(bool enabled) override;
259 
260 private:
261     // Protect access to mMotionClassifier, since it may become null via a hidl callback
262     std::mutex mLock;
263     // The next stage to pass input events to
264     QueuedInputListener mQueuedListener;
265 
266     std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
267     std::future<void> mInitializeMotionClassifier GUARDED_BY(mLock);
268 
269     /**
270      * Set the value of mMotionClassifier.
271      * This is called from 2 different threads:
272      * 1) mInitializeMotionClassifierThread, when we have constructed a MotionClassifier
273      * 2) A binder thread of the HalDeathRecipient, which is created when HAL dies. This would cause
274      *    mMotionClassifier to become nullptr.
275      */
276     void setMotionClassifierLocked(std::unique_ptr<MotionClassifierInterface> motionClassifier)
277             REQUIRES(mLock);
278 
279     static void onBinderDied(void* cookie);
280 
281     std::unique_ptr<ScopedDeathRecipient> mHalDeathRecipient GUARDED_BY(mLock);
282 };
283 
284 } // namespace android
285 #endif
286