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