Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 04c5e071 authored by Siarhei Vishniakou's avatar Siarhei Vishniakou Committed by Automerger Merge Worker
Browse files

Merge "Properly initialize MotionClassifier" into rvc-dev am: e636f3fe

Change-Id: I6dfd20ab22090efa6a9fd05b6843f414b48af9aa
parents 0e90cf19 e636f3fe
Loading
Loading
Loading
Loading
+53 −81
Original line number Original line Diff line number Diff line
@@ -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
@@ -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() {
@@ -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).
@@ -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;
@@ -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) {
@@ -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) {
@@ -472,4 +440,8 @@ void InputClassifier::dump(std::string& dump) {
    dump += "\n";
    dump += "\n";
}
}


InputClassifier::~InputClassifier() {
    mInitializeMotionClassifierThread.join();
}

} // namespace android
} // namespace android
+50 −38
Original line number Original line Diff line number Diff line
@@ -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"
@@ -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.
@@ -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;
@@ -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
@@ -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;
@@ -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
+32 −1
Original line number Original line Diff line number Diff line
@@ -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 {


@@ -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 {
@@ -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()));
        }
    }
    }
};
};