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