Loading services/inputflinger/InputClassifier.cpp +53 −81 Original line number Original line Diff line number Diff line Loading @@ -46,8 +46,6 @@ using namespace android::hardware::input; namespace android { namespace android { static constexpr bool DEBUG = false; // Category (=namespace) name for the input settings that are applied at boot time // Category (=namespace) name for the input settings that are applied at boot time static const char* INPUT_NATIVE_BOOT = "input_native_boot"; static const char* INPUT_NATIVE_BOOT = "input_native_boot"; // Feature flag name for the deep press feature // Feature flag name for the deep press feature Loading Loading @@ -141,53 +139,46 @@ std::optional<int32_t> ClassifierEvent::getDeviceId() const { // --- MotionClassifier --- // --- MotionClassifier --- MotionClassifier::MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient) : MotionClassifier::MotionClassifier( mDeathRecipient(deathRecipient), mEvents(MAX_EVENTS) { sp<android::hardware::input::classifier::V1_0::IInputClassifier> service) mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this); : mEvents(MAX_EVENTS), mService(service) { // Under normal operation, we do not need to reset the HAL here. But in the case where system // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already // have received events in the past. That means, that HAL could be in an inconsistent state // once it receives events from the newly created MotionClassifier. mEvents.push(ClassifierEvent::createHalResetEvent()); mHalThread = std::thread(&MotionClassifier::processEvents, this); #if defined(__linux__) #if defined(__linux__) // Set the thread name for debugging // Set the thread name for debugging pthread_setname_np(mHalThread.native_handle(), "InputClassifier"); pthread_setname_np(mHalThread.native_handle(), "InputClassifier"); #endif #endif } } /** std::unique_ptr<MotionClassifierInterface> MotionClassifier::create( * This function may block for some time to initialize the HAL, so it should only be called sp<android::hardware::hidl_death_recipient> deathRecipient) { * from the "InputClassifier HAL" thread. if (!deepPressEnabled()) { */ // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work. bool MotionClassifier::init() { // When MotionClassifier is null, InputClassifier will forward all events ensureHalThread(__func__); // to the next InputListener, unmodified. return nullptr; } sp<android::hardware::input::classifier::V1_0::IInputClassifier> service = sp<android::hardware::input::classifier::V1_0::IInputClassifier> service = classifier::V1_0::IInputClassifier::getService(); classifier::V1_0::IInputClassifier::getService(); if (!service) { if (!service) { // Not really an error, maybe the device does not have this HAL, // Not really an error, maybe the device does not have this HAL, // but somehow the feature flag is flipped // but somehow the feature flag is flipped ALOGI("Could not obtain InputClassifier HAL"); ALOGI("Could not obtain InputClassifier HAL"); return false; return nullptr; } } sp<android::hardware::hidl_death_recipient> recipient = mDeathRecipient.promote(); const bool linked = service->linkToDeath(deathRecipient, 0 /* cookie */).withDefault(false); if (recipient != nullptr) { const bool linked = service->linkToDeath(recipient, 0 /* cookie */).withDefault(false); if (!linked) { if (!linked) { ALOGE("Could not link MotionClassifier to the HAL death"); ALOGE("Could not link death recipient to the HAL death"); return false; return nullptr; } } // Under normal operation, we do not need to reset the HAL here. But in the case where system // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already // have received events in the past. That means, that HAL could be in an inconsistent state // once it receives events from the newly created MotionClassifier. mEvents.push(ClassifierEvent::createHalResetEvent()); { std::scoped_lock lock(mLock); if (mService) { ALOGE("MotionClassifier::%s should only be called once", __func__); } } mService = service; // Using 'new' to access a non-public constructor } return std::unique_ptr<MotionClassifier>(new MotionClassifier(service)); return true; } } MotionClassifier::~MotionClassifier() { MotionClassifier::~MotionClassifier() { Loading @@ -195,14 +186,6 @@ MotionClassifier::~MotionClassifier() { mHalThread.join(); mHalThread.join(); } } void MotionClassifier::ensureHalThread(const char* function) { if (DEBUG) { if (std::this_thread::get_id() != mHalThread.get_id()) { LOG_FATAL("Function %s should only be called from InputClassifier thread", function); } } } /** /** * Obtain the classification from the HAL for a given MotionEvent. * Obtain the classification from the HAL for a given MotionEvent. * Should only be called from the InputClassifier thread (mHalThread). * Should only be called from the InputClassifier thread (mHalThread). Loading @@ -213,23 +196,7 @@ void MotionClassifier::ensureHalThread(const char* function) { * To remove any possibility of negatively affecting the touch latency, the HAL * To remove any possibility of negatively affecting the touch latency, the HAL * is called from a dedicated thread. * is called from a dedicated thread. */ */ void MotionClassifier::callInputClassifierHal() { void MotionClassifier::processEvents() { ensureHalThread(__func__); const bool initialized = init(); if (!initialized) { // MotionClassifier no longer useful. // Deliver death notification from a separate thread // because ~MotionClassifier may be invoked, which calls mHalThread.join() std::thread([deathRecipient = mDeathRecipient](){ sp<android::hardware::hidl_death_recipient> recipient = deathRecipient.promote(); if (recipient != nullptr) { recipient->serviceDied(0 /*cookie*/, nullptr); } }).detach(); return; } // From this point on, mService is guaranteed to be non-null. while (true) { while (true) { ClassifierEvent event = mEvents.pop(); ClassifierEvent event = mEvents.pop(); bool halResponseOk = true; bool halResponseOk = true; Loading Loading @@ -389,24 +356,30 @@ void MotionClassifier::dump(std::string& dump) { } } } } // --- HalDeathRecipient // --- InputClassifier --- InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {} InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) : void InputClassifier::HalDeathRecipient::serviceDied( mListener(listener) { uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) { // The rest of the initialization is done in onFirstRef, because we need to obtain sp<android::hidl::base::V1_0::IBase> service = who.promote(); // an sp to 'this' in order to register for HAL death notifications if (service) { service->unlinkToDeath(this); } } mParent.setMotionClassifier(nullptr); void InputClassifier::onFirstRef() { if (!deepPressEnabled()) { // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work. // When MotionClassifier is null, InputClassifier will forward all events // to the next InputListener, unmodified. return; } } std::scoped_lock lock(mLock); mMotionClassifier = std::make_unique<MotionClassifier>(this); // --- InputClassifier --- InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) { mInitializeMotionClassifierThread = std::thread( [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); }); #if defined(__linux__) // Set the thread name for debugging pthread_setname_np(mInitializeMotionClassifierThread.native_handle(), "Create MotionClassifier"); #endif } } void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { Loading Loading @@ -447,15 +420,10 @@ void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { mListener->notifyDeviceReset(args); mListener->notifyDeviceReset(args); } } void InputClassifier::serviceDied(uint64_t /*cookie*/, void InputClassifier::setMotionClassifier( const wp<android::hidl::base::V1_0::IBase>& who) { std::unique_ptr<MotionClassifierInterface> motionClassifier) { std::scoped_lock lock(mLock); std::scoped_lock lock(mLock); ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null"); mMotionClassifier = std::move(motionClassifier); mMotionClassifier = nullptr; sp<android::hidl::base::V1_0::IBase> service = who.promote(); if (service) { service->unlinkToDeath(this); } } } void InputClassifier::dump(std::string& dump) { void InputClassifier::dump(std::string& dump) { Loading @@ -472,4 +440,8 @@ void InputClassifier::dump(std::string& dump) { dump += "\n"; dump += "\n"; } } InputClassifier::~InputClassifier() { mInitializeMotionClassifierThread.join(); } } // namespace android } // namespace android services/inputflinger/InputClassifier.h +50 −38 Original line number Original line Diff line number Diff line Loading @@ -19,8 +19,8 @@ #include <android-base/thread_annotations.h> #include <android-base/thread_annotations.h> #include <utils/RefBase.h> #include <utils/RefBase.h> #include <unordered_map> #include <thread> #include <thread> #include <unordered_map> #include "BlockingQueue.h" #include "BlockingQueue.h" #include "InputListener.h" #include "InputListener.h" Loading Loading @@ -113,23 +113,23 @@ protected: */ */ class MotionClassifier final : public MotionClassifierInterface { class MotionClassifier final : public MotionClassifierInterface { public: public: /** /* * The deathRecipient will be subscribed to the HAL death. If the death recipient * Create an instance of MotionClassifier. * owns MotionClassifier and receives HAL death, it should delete its copy of it. * The death recipient, if provided, will be subscribed to the HAL death. * The callback serviceDied will also be sent if the MotionClassifier itself fails * The death recipient could be used to destroy MotionClassifier. * to initialize. If the MotionClassifier fails to initialize, it is not useful, and * * should be deleted. * This function should be called asynchronously, because getService takes a long time. * If no death recipient is supplied, then the registration step will be skipped, so there will * be no listeners registered for the HAL death. This is useful for testing * MotionClassifier in isolation. */ */ explicit MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient = nullptr); static std::unique_ptr<MotionClassifierInterface> create( sp<android::hardware::hidl_death_recipient> deathRecipient); ~MotionClassifier(); ~MotionClassifier(); /** /** * Classifies events asynchronously; that is, it doesn't block events on a classification, * Classifies events asynchronously; that is, it doesn't block events on a classification, * but instead sends them over to the classifier HAL and after a classification is * but instead sends them over to the classifier HAL. After a classification of a specific * determined, it then marks the next event it sees in the stream with it. * 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 * Therefore, it is acceptable to have the classifications be delayed by 1-2 events * in a particular gesture. * in a particular gesture. Loading @@ -141,15 +141,9 @@ public: virtual void dump(std::string& dump) override; virtual void dump(std::string& dump) override; private: private: /** friend class MotionClassifierTest; // to create MotionClassifier with a test HAL implementation * Initialize MotionClassifier. explicit MotionClassifier( * Return true if initializaion is successful. sp<android::hardware::input::classifier::V1_0::IInputClassifier> service); */ bool init(); /** * Entity that will be notified of the HAL death (most likely InputClassifier). */ wp<android::hardware::hidl_death_recipient> mDeathRecipient; // The events that need to be sent to the HAL. // The events that need to be sent to the HAL. BlockingQueue<ClassifierEvent> mEvents; BlockingQueue<ClassifierEvent> mEvents; Loading @@ -164,14 +158,9 @@ private: */ */ std::thread mHalThread; std::thread mHalThread; /** /** * Print an error message if the caller is not on the InputClassifier thread. * Process events and call the InputClassifier HAL * Caller must supply the name of the calling function as __func__ */ void ensureHalThread(const char* function); /** * Call the InputClassifier HAL */ */ void callInputClassifierHal(); void processEvents(); /** /** * Access to the InputClassifier HAL. May be null if init() hasn't completed yet. * Access to the InputClassifier HAL. May be null if init() hasn't completed yet. * When init() successfully completes, mService is guaranteed to remain non-null and to not * When init() successfully completes, mService is guaranteed to remain non-null and to not Loading Loading @@ -225,19 +214,15 @@ private: const char* getServiceStatus() REQUIRES(mLock); const char* getServiceStatus() REQUIRES(mLock); }; }; /** /** * Implementation of the InputClassifierInterface. * Implementation of the InputClassifierInterface. * Represents a separate stage of input processing. All of the input events go through this stage. * 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. * Acts as a passthrough for all input events except for motion events. * The events of motion type are sent to MotionClassifier. * The events of motion type are sent to MotionClassifier. */ */ class InputClassifier : public InputClassifierInterface, class InputClassifier : public InputClassifierInterface { public android::hardware::hidl_death_recipient { public: public: explicit InputClassifier(const sp<InputListenerInterface>& listener); explicit InputClassifier(const sp<InputListenerInterface>& listener); // Some of the constructor logic is finished in onFirstRef virtual void onFirstRef() override; virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; virtual void notifyKey(const NotifyKeyArgs* args) override; virtual void notifyKey(const NotifyKeyArgs* args) override; Loading @@ -245,17 +230,44 @@ public: virtual void notifySwitch(const NotifySwitchArgs* args) override; virtual void notifySwitch(const NotifySwitchArgs* args) override; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; virtual void serviceDied(uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) override; virtual void dump(std::string& dump) override; virtual void dump(std::string& dump) override; ~InputClassifier(); private: private: // Protect access to mMotionClassifier, since it may become null via a hidl callback // Protect access to mMotionClassifier, since it may become null via a hidl callback std::mutex mLock; std::mutex mLock; std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock); // The next stage to pass input events to // The next stage to pass input events to sp<InputListenerInterface> mListener; sp<InputListenerInterface> mListener; std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock); std::thread mInitializeMotionClassifierThread; /** * 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 setMotionClassifier(std::unique_ptr<MotionClassifierInterface> motionClassifier); /** * The deathRecipient will call setMotionClassifier(null) when the HAL dies. */ class HalDeathRecipient : public android::hardware::hidl_death_recipient { public: explicit HalDeathRecipient(InputClassifier& parent); virtual void serviceDied(uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) override; private: InputClassifier& mParent; }; // We retain a reference to death recipient, because the death recipient will be calling // ~MotionClassifier if the HAL dies. // If we don't retain a reference, and MotionClassifier is the only owner of the death // recipient, the serviceDied call will cause death recipient to call its own destructor. sp<HalDeathRecipient> mHalDeathRecipient; }; }; } // namespace android } // namespace android Loading services/inputflinger/tests/InputClassifier_test.cpp +32 −1 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,9 @@ #include <android/hardware/input/classifier/1.0/IInputClassifier.h> #include <android/hardware/input/classifier/1.0/IInputClassifier.h> using namespace android::hardware::input; using namespace android::hardware::input; using android::hardware::Return; using android::hardware::Void; using android::hardware::input::common::V1_0::Classification; namespace android { namespace android { Loading Loading @@ -132,6 +135,27 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifyDeviceResetArgs) { ASSERT_EQ(args, outArgs); ASSERT_EQ(args, outArgs); } } /** * A minimal implementation of IInputClassifier. */ struct TestHal : public android::hardware::input::classifier::V1_0::IInputClassifier { Return<Classification> classify( const android::hardware::input::common::V1_0::MotionEvent& event) override { return Classification::NONE; }; Return<void> reset() override { return Void(); }; Return<void> resetDevice(int32_t deviceId) override { return Void(); }; }; /** * An entity that will be subscribed to the HAL death. */ class TestDeathRecipient : public android::hardware::hidl_death_recipient { public: virtual void serviceDied(uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) override{}; }; // --- MotionClassifierTest --- // --- MotionClassifierTest --- class MotionClassifierTest : public testing::Test { class MotionClassifierTest : public testing::Test { Loading @@ -139,7 +163,14 @@ protected: std::unique_ptr<MotionClassifierInterface> mMotionClassifier; std::unique_ptr<MotionClassifierInterface> mMotionClassifier; virtual void SetUp() override { virtual void SetUp() override { mMotionClassifier = std::make_unique<MotionClassifier>(); mMotionClassifier = MotionClassifier::create(new TestDeathRecipient()); if (mMotionClassifier == nullptr) { // If the device running this test does not have IInputClassifier service, // use the test HAL instead. // Using 'new' to access non-public constructor mMotionClassifier = std::unique_ptr<MotionClassifier>(new MotionClassifier(new TestHal())); } } } }; }; Loading Loading
services/inputflinger/InputClassifier.cpp +53 −81 Original line number Original line Diff line number Diff line Loading @@ -46,8 +46,6 @@ using namespace android::hardware::input; namespace android { namespace android { static constexpr bool DEBUG = false; // Category (=namespace) name for the input settings that are applied at boot time // Category (=namespace) name for the input settings that are applied at boot time static const char* INPUT_NATIVE_BOOT = "input_native_boot"; static const char* INPUT_NATIVE_BOOT = "input_native_boot"; // Feature flag name for the deep press feature // Feature flag name for the deep press feature Loading Loading @@ -141,53 +139,46 @@ std::optional<int32_t> ClassifierEvent::getDeviceId() const { // --- MotionClassifier --- // --- MotionClassifier --- MotionClassifier::MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient) : MotionClassifier::MotionClassifier( mDeathRecipient(deathRecipient), mEvents(MAX_EVENTS) { sp<android::hardware::input::classifier::V1_0::IInputClassifier> service) mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this); : mEvents(MAX_EVENTS), mService(service) { // Under normal operation, we do not need to reset the HAL here. But in the case where system // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already // have received events in the past. That means, that HAL could be in an inconsistent state // once it receives events from the newly created MotionClassifier. mEvents.push(ClassifierEvent::createHalResetEvent()); mHalThread = std::thread(&MotionClassifier::processEvents, this); #if defined(__linux__) #if defined(__linux__) // Set the thread name for debugging // Set the thread name for debugging pthread_setname_np(mHalThread.native_handle(), "InputClassifier"); pthread_setname_np(mHalThread.native_handle(), "InputClassifier"); #endif #endif } } /** std::unique_ptr<MotionClassifierInterface> MotionClassifier::create( * This function may block for some time to initialize the HAL, so it should only be called sp<android::hardware::hidl_death_recipient> deathRecipient) { * from the "InputClassifier HAL" thread. if (!deepPressEnabled()) { */ // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work. bool MotionClassifier::init() { // When MotionClassifier is null, InputClassifier will forward all events ensureHalThread(__func__); // to the next InputListener, unmodified. return nullptr; } sp<android::hardware::input::classifier::V1_0::IInputClassifier> service = sp<android::hardware::input::classifier::V1_0::IInputClassifier> service = classifier::V1_0::IInputClassifier::getService(); classifier::V1_0::IInputClassifier::getService(); if (!service) { if (!service) { // Not really an error, maybe the device does not have this HAL, // Not really an error, maybe the device does not have this HAL, // but somehow the feature flag is flipped // but somehow the feature flag is flipped ALOGI("Could not obtain InputClassifier HAL"); ALOGI("Could not obtain InputClassifier HAL"); return false; return nullptr; } } sp<android::hardware::hidl_death_recipient> recipient = mDeathRecipient.promote(); const bool linked = service->linkToDeath(deathRecipient, 0 /* cookie */).withDefault(false); if (recipient != nullptr) { const bool linked = service->linkToDeath(recipient, 0 /* cookie */).withDefault(false); if (!linked) { if (!linked) { ALOGE("Could not link MotionClassifier to the HAL death"); ALOGE("Could not link death recipient to the HAL death"); return false; return nullptr; } } // Under normal operation, we do not need to reset the HAL here. But in the case where system // crashed, but HAL didn't, we may be connecting to an existing HAL process that might already // have received events in the past. That means, that HAL could be in an inconsistent state // once it receives events from the newly created MotionClassifier. mEvents.push(ClassifierEvent::createHalResetEvent()); { std::scoped_lock lock(mLock); if (mService) { ALOGE("MotionClassifier::%s should only be called once", __func__); } } mService = service; // Using 'new' to access a non-public constructor } return std::unique_ptr<MotionClassifier>(new MotionClassifier(service)); return true; } } MotionClassifier::~MotionClassifier() { MotionClassifier::~MotionClassifier() { Loading @@ -195,14 +186,6 @@ MotionClassifier::~MotionClassifier() { mHalThread.join(); mHalThread.join(); } } void MotionClassifier::ensureHalThread(const char* function) { if (DEBUG) { if (std::this_thread::get_id() != mHalThread.get_id()) { LOG_FATAL("Function %s should only be called from InputClassifier thread", function); } } } /** /** * Obtain the classification from the HAL for a given MotionEvent. * Obtain the classification from the HAL for a given MotionEvent. * Should only be called from the InputClassifier thread (mHalThread). * Should only be called from the InputClassifier thread (mHalThread). Loading @@ -213,23 +196,7 @@ void MotionClassifier::ensureHalThread(const char* function) { * To remove any possibility of negatively affecting the touch latency, the HAL * To remove any possibility of negatively affecting the touch latency, the HAL * is called from a dedicated thread. * is called from a dedicated thread. */ */ void MotionClassifier::callInputClassifierHal() { void MotionClassifier::processEvents() { ensureHalThread(__func__); const bool initialized = init(); if (!initialized) { // MotionClassifier no longer useful. // Deliver death notification from a separate thread // because ~MotionClassifier may be invoked, which calls mHalThread.join() std::thread([deathRecipient = mDeathRecipient](){ sp<android::hardware::hidl_death_recipient> recipient = deathRecipient.promote(); if (recipient != nullptr) { recipient->serviceDied(0 /*cookie*/, nullptr); } }).detach(); return; } // From this point on, mService is guaranteed to be non-null. while (true) { while (true) { ClassifierEvent event = mEvents.pop(); ClassifierEvent event = mEvents.pop(); bool halResponseOk = true; bool halResponseOk = true; Loading Loading @@ -389,24 +356,30 @@ void MotionClassifier::dump(std::string& dump) { } } } } // --- HalDeathRecipient // --- InputClassifier --- InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {} InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) : void InputClassifier::HalDeathRecipient::serviceDied( mListener(listener) { uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) { // The rest of the initialization is done in onFirstRef, because we need to obtain sp<android::hidl::base::V1_0::IBase> service = who.promote(); // an sp to 'this' in order to register for HAL death notifications if (service) { service->unlinkToDeath(this); } } mParent.setMotionClassifier(nullptr); void InputClassifier::onFirstRef() { if (!deepPressEnabled()) { // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work. // When MotionClassifier is null, InputClassifier will forward all events // to the next InputListener, unmodified. return; } } std::scoped_lock lock(mLock); mMotionClassifier = std::make_unique<MotionClassifier>(this); // --- InputClassifier --- InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) { mInitializeMotionClassifierThread = std::thread( [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); }); #if defined(__linux__) // Set the thread name for debugging pthread_setname_np(mInitializeMotionClassifierThread.native_handle(), "Create MotionClassifier"); #endif } } void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { Loading Loading @@ -447,15 +420,10 @@ void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { mListener->notifyDeviceReset(args); mListener->notifyDeviceReset(args); } } void InputClassifier::serviceDied(uint64_t /*cookie*/, void InputClassifier::setMotionClassifier( const wp<android::hidl::base::V1_0::IBase>& who) { std::unique_ptr<MotionClassifierInterface> motionClassifier) { std::scoped_lock lock(mLock); std::scoped_lock lock(mLock); ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null"); mMotionClassifier = std::move(motionClassifier); mMotionClassifier = nullptr; sp<android::hidl::base::V1_0::IBase> service = who.promote(); if (service) { service->unlinkToDeath(this); } } } void InputClassifier::dump(std::string& dump) { void InputClassifier::dump(std::string& dump) { Loading @@ -472,4 +440,8 @@ void InputClassifier::dump(std::string& dump) { dump += "\n"; dump += "\n"; } } InputClassifier::~InputClassifier() { mInitializeMotionClassifierThread.join(); } } // namespace android } // namespace android
services/inputflinger/InputClassifier.h +50 −38 Original line number Original line Diff line number Diff line Loading @@ -19,8 +19,8 @@ #include <android-base/thread_annotations.h> #include <android-base/thread_annotations.h> #include <utils/RefBase.h> #include <utils/RefBase.h> #include <unordered_map> #include <thread> #include <thread> #include <unordered_map> #include "BlockingQueue.h" #include "BlockingQueue.h" #include "InputListener.h" #include "InputListener.h" Loading Loading @@ -113,23 +113,23 @@ protected: */ */ class MotionClassifier final : public MotionClassifierInterface { class MotionClassifier final : public MotionClassifierInterface { public: public: /** /* * The deathRecipient will be subscribed to the HAL death. If the death recipient * Create an instance of MotionClassifier. * owns MotionClassifier and receives HAL death, it should delete its copy of it. * The death recipient, if provided, will be subscribed to the HAL death. * The callback serviceDied will also be sent if the MotionClassifier itself fails * The death recipient could be used to destroy MotionClassifier. * to initialize. If the MotionClassifier fails to initialize, it is not useful, and * * should be deleted. * This function should be called asynchronously, because getService takes a long time. * If no death recipient is supplied, then the registration step will be skipped, so there will * be no listeners registered for the HAL death. This is useful for testing * MotionClassifier in isolation. */ */ explicit MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient = nullptr); static std::unique_ptr<MotionClassifierInterface> create( sp<android::hardware::hidl_death_recipient> deathRecipient); ~MotionClassifier(); ~MotionClassifier(); /** /** * Classifies events asynchronously; that is, it doesn't block events on a classification, * Classifies events asynchronously; that is, it doesn't block events on a classification, * but instead sends them over to the classifier HAL and after a classification is * but instead sends them over to the classifier HAL. After a classification of a specific * determined, it then marks the next event it sees in the stream with it. * 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 * Therefore, it is acceptable to have the classifications be delayed by 1-2 events * in a particular gesture. * in a particular gesture. Loading @@ -141,15 +141,9 @@ public: virtual void dump(std::string& dump) override; virtual void dump(std::string& dump) override; private: private: /** friend class MotionClassifierTest; // to create MotionClassifier with a test HAL implementation * Initialize MotionClassifier. explicit MotionClassifier( * Return true if initializaion is successful. sp<android::hardware::input::classifier::V1_0::IInputClassifier> service); */ bool init(); /** * Entity that will be notified of the HAL death (most likely InputClassifier). */ wp<android::hardware::hidl_death_recipient> mDeathRecipient; // The events that need to be sent to the HAL. // The events that need to be sent to the HAL. BlockingQueue<ClassifierEvent> mEvents; BlockingQueue<ClassifierEvent> mEvents; Loading @@ -164,14 +158,9 @@ private: */ */ std::thread mHalThread; std::thread mHalThread; /** /** * Print an error message if the caller is not on the InputClassifier thread. * Process events and call the InputClassifier HAL * Caller must supply the name of the calling function as __func__ */ void ensureHalThread(const char* function); /** * Call the InputClassifier HAL */ */ void callInputClassifierHal(); void processEvents(); /** /** * Access to the InputClassifier HAL. May be null if init() hasn't completed yet. * Access to the InputClassifier HAL. May be null if init() hasn't completed yet. * When init() successfully completes, mService is guaranteed to remain non-null and to not * When init() successfully completes, mService is guaranteed to remain non-null and to not Loading Loading @@ -225,19 +214,15 @@ private: const char* getServiceStatus() REQUIRES(mLock); const char* getServiceStatus() REQUIRES(mLock); }; }; /** /** * Implementation of the InputClassifierInterface. * Implementation of the InputClassifierInterface. * Represents a separate stage of input processing. All of the input events go through this stage. * 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. * Acts as a passthrough for all input events except for motion events. * The events of motion type are sent to MotionClassifier. * The events of motion type are sent to MotionClassifier. */ */ class InputClassifier : public InputClassifierInterface, class InputClassifier : public InputClassifierInterface { public android::hardware::hidl_death_recipient { public: public: explicit InputClassifier(const sp<InputListenerInterface>& listener); explicit InputClassifier(const sp<InputListenerInterface>& listener); // Some of the constructor logic is finished in onFirstRef virtual void onFirstRef() override; virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; virtual void notifyKey(const NotifyKeyArgs* args) override; virtual void notifyKey(const NotifyKeyArgs* args) override; Loading @@ -245,17 +230,44 @@ public: virtual void notifySwitch(const NotifySwitchArgs* args) override; virtual void notifySwitch(const NotifySwitchArgs* args) override; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; virtual void serviceDied(uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) override; virtual void dump(std::string& dump) override; virtual void dump(std::string& dump) override; ~InputClassifier(); private: private: // Protect access to mMotionClassifier, since it may become null via a hidl callback // Protect access to mMotionClassifier, since it may become null via a hidl callback std::mutex mLock; std::mutex mLock; std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock); // The next stage to pass input events to // The next stage to pass input events to sp<InputListenerInterface> mListener; sp<InputListenerInterface> mListener; std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock); std::thread mInitializeMotionClassifierThread; /** * 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 setMotionClassifier(std::unique_ptr<MotionClassifierInterface> motionClassifier); /** * The deathRecipient will call setMotionClassifier(null) when the HAL dies. */ class HalDeathRecipient : public android::hardware::hidl_death_recipient { public: explicit HalDeathRecipient(InputClassifier& parent); virtual void serviceDied(uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) override; private: InputClassifier& mParent; }; // We retain a reference to death recipient, because the death recipient will be calling // ~MotionClassifier if the HAL dies. // If we don't retain a reference, and MotionClassifier is the only owner of the death // recipient, the serviceDied call will cause death recipient to call its own destructor. sp<HalDeathRecipient> mHalDeathRecipient; }; }; } // namespace android } // namespace android Loading
services/inputflinger/tests/InputClassifier_test.cpp +32 −1 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,9 @@ #include <android/hardware/input/classifier/1.0/IInputClassifier.h> #include <android/hardware/input/classifier/1.0/IInputClassifier.h> using namespace android::hardware::input; using namespace android::hardware::input; using android::hardware::Return; using android::hardware::Void; using android::hardware::input::common::V1_0::Classification; namespace android { namespace android { Loading Loading @@ -132,6 +135,27 @@ TEST_F(InputClassifierTest, SendToNextStage_NotifyDeviceResetArgs) { ASSERT_EQ(args, outArgs); ASSERT_EQ(args, outArgs); } } /** * A minimal implementation of IInputClassifier. */ struct TestHal : public android::hardware::input::classifier::V1_0::IInputClassifier { Return<Classification> classify( const android::hardware::input::common::V1_0::MotionEvent& event) override { return Classification::NONE; }; Return<void> reset() override { return Void(); }; Return<void> resetDevice(int32_t deviceId) override { return Void(); }; }; /** * An entity that will be subscribed to the HAL death. */ class TestDeathRecipient : public android::hardware::hidl_death_recipient { public: virtual void serviceDied(uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) override{}; }; // --- MotionClassifierTest --- // --- MotionClassifierTest --- class MotionClassifierTest : public testing::Test { class MotionClassifierTest : public testing::Test { Loading @@ -139,7 +163,14 @@ protected: std::unique_ptr<MotionClassifierInterface> mMotionClassifier; std::unique_ptr<MotionClassifierInterface> mMotionClassifier; virtual void SetUp() override { virtual void SetUp() override { mMotionClassifier = std::make_unique<MotionClassifier>(); mMotionClassifier = MotionClassifier::create(new TestDeathRecipient()); if (mMotionClassifier == nullptr) { // If the device running this test does not have IInputClassifier service, // use the test HAL instead. // Using 'new' to access non-public constructor mMotionClassifier = std::unique_ptr<MotionClassifier>(new MotionClassifier(new TestHal())); } } } }; }; Loading