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

Commit 2574dfa1 authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

Make InputReader test components thread safe

This changes all assert*() methods in the following classes to enable
waiting for the assertion to be true:
- FakeInputReaderPolicy
- FakeEventHub
- FakeInputMapper
- TestInputListener

This change should make InputReader robust to being run on another
thread.

Bug: 130819454
Test: atest inputflinger_tests # on crosshatch
Change-Id: I9619109e3377eb706d71fb34d525bd3bdc4ef81a
parent 72f94e1d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ cc_test {
        "-Werror",
        "-Wextra",
        "-Wno-unused-parameter",
        "-Wthread-safety",
    ],
    shared_libs: [
        "android.hardware.input.classifier@1.0",
+100 −33
Original line number Diff line number Diff line
@@ -25,13 +25,18 @@
#include <TestInputListener.h>
#include <TouchInputMapper.h>

#include <android-base/thread_annotations.h>
#include <gtest/gtest.h>
#include <inttypes.h>
#include <math.h>


namespace android {

using std::chrono_literals::operator""ms;

// Timeout for waiting for an expected event
static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;

// An arbitrary time value.
static const nsecs_t ARBITRARY_TIME = 1234;

@@ -164,9 +169,13 @@ private:
// --- FakeInputReaderPolicy ---

class FakeInputReaderPolicy : public InputReaderPolicyInterface {
    std::mutex mLock;
    std::condition_variable mDevicesChangedCondition;

    InputReaderConfiguration mConfig;
    KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers;
    std::vector<InputDeviceInfo> mInputDevices;
    std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
    bool mInputDevicesChanged GUARDED_BY(mLock){false};
    std::vector<DisplayViewport> mViewports;
    TouchAffineTransformation transform;

@@ -177,6 +186,20 @@ public:
    FakeInputReaderPolicy() {
    }

    void assertInputDevicesChanged() {
        std::unique_lock<std::mutex> lock(mLock);
        base::ScopedLockAssertion assumeLocked(mLock);

        const bool devicesChanged =
                mDevicesChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
                    return mInputDevicesChanged;
                });
        if (!devicesChanged) {
            FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
        }
        mInputDevicesChanged = false;
    }

    virtual void clearViewports() {
        mViewports.clear();
        mConfig.setDisplayViewports(mViewports);
@@ -291,7 +314,10 @@ private:
    }

    virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
        std::scoped_lock<std::mutex> lock(mLock);
        mInputDevices = inputDevices;
        mInputDevicesChanged = true;
        mDevicesChangedCondition.notify_all();
    }

    virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier&) {
@@ -342,9 +368,12 @@ class FakeEventHub : public EventHubInterface {
        }
    };

    std::mutex mLock;
    std::condition_variable mEventsCondition;

    KeyedVector<int32_t, Device*> mDevices;
    std::vector<std::string> mExcludedDevices;
    List<RawEvent> mEvents;
    List<RawEvent> mEvents GUARDED_BY(mLock);
    std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames;

public:
@@ -496,6 +525,7 @@ public:

    void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type,
            int32_t code, int32_t value) {
        std::scoped_lock<std::mutex> lock(mLock);
        RawEvent event;
        event.when = when;
        event.deviceId = deviceId;
@@ -515,8 +545,14 @@ public:
    }

    void assertQueueIsEmpty() {
        ASSERT_EQ(size_t(0), mEvents.size())
                << "Expected the event queue to be empty (fully consumed).";
        std::unique_lock<std::mutex> lock(mLock);
        base::ScopedLockAssertion assumeLocked(mLock);
        const bool queueIsEmpty =
                mEventsCondition.wait_for(lock, WAIT_TIMEOUT,
                                          [this]() REQUIRES(mLock) { return mEvents.size() == 0; });
        if (!queueIsEmpty) {
            FAIL() << "Timed out waiting for EventHub queue to be emptied.";
        }
    }

private:
@@ -619,12 +655,14 @@ private:
    }

    virtual size_t getEvents(int, RawEvent* buffer, size_t) {
        std::scoped_lock<std::mutex> lock(mLock);
        if (mEvents.empty()) {
            return 0;
        }

        *buffer = *mEvents.begin();
        mEvents.erase(mEvents.begin());
        mEventsCondition.notify_all();
        return 1;
    }

@@ -877,11 +915,13 @@ class FakeInputMapper : public InputMapper {
    KeyedVector<int32_t, int32_t> mScanCodeStates;
    KeyedVector<int32_t, int32_t> mSwitchStates;
    std::vector<int32_t> mSupportedKeyCodes;
    RawEvent mLastEvent;

    bool mConfigureWasCalled;
    bool mResetWasCalled;
    bool mProcessWasCalled;
    std::mutex mLock;
    std::condition_variable mStateChangedCondition;
    bool mConfigureWasCalled GUARDED_BY(mLock);
    bool mResetWasCalled GUARDED_BY(mLock);
    bool mProcessWasCalled GUARDED_BY(mLock);
    RawEvent mLastEvent GUARDED_BY(mLock);

    std::optional<DisplayViewport> mViewport;
public:
@@ -903,20 +943,41 @@ public:
    }

    void assertConfigureWasCalled() {
        ASSERT_TRUE(mConfigureWasCalled)
                << "Expected configure() to have been called.";
        std::unique_lock<std::mutex> lock(mLock);
        base::ScopedLockAssertion assumeLocked(mLock);
        const bool configureCalled =
                mStateChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
                    return mConfigureWasCalled;
                });
        if (!configureCalled) {
            FAIL() << "Expected configure() to have been called.";
        }
        mConfigureWasCalled = false;
    }

    void assertResetWasCalled() {
        ASSERT_TRUE(mResetWasCalled)
                << "Expected reset() to have been called.";
        std::unique_lock<std::mutex> lock(mLock);
        base::ScopedLockAssertion assumeLocked(mLock);
        const bool resetCalled =
                mStateChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
                    return mResetWasCalled;
                });
        if (!resetCalled) {
            FAIL() << "Expected reset() to have been called.";
        }
        mResetWasCalled = false;
    }

    void assertProcessWasCalled(RawEvent* outLastEvent = nullptr) {
        ASSERT_TRUE(mProcessWasCalled)
                << "Expected process() to have been called.";
        std::unique_lock<std::mutex> lock(mLock);
        base::ScopedLockAssertion assumeLocked(mLock);
        const bool processCalled =
                mStateChangedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) {
                    return mProcessWasCalled;
                });
        if (!processCalled) {
            FAIL() << "Expected process() to have been called.";
        }
        if (outLastEvent) {
            *outLastEvent = mLastEvent;
        }
@@ -953,6 +1014,7 @@ private:
    }

    virtual void configure(nsecs_t, const InputReaderConfiguration* config, uint32_t changes) {
        std::scoped_lock<std::mutex> lock(mLock);
        mConfigureWasCalled = true;

        // Find the associated viewport if exist.
@@ -960,15 +1022,21 @@ private:
        if (displayPort && (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
            mViewport = config->getDisplayViewportByPort(*displayPort);
        }

        mStateChangedCondition.notify_all();
    }

    virtual void reset(nsecs_t) {
        std::scoped_lock<std::mutex> lock(mLock);
        mResetWasCalled = true;
        mStateChangedCondition.notify_all();
    }

    virtual void process(const RawEvent* rawEvent) {
        std::scoped_lock<std::mutex> lock(mLock);
        mLastEvent = *rawEvent;
        mProcessWasCalled = true;
        mStateChangedCondition.notify_all();
    }

    virtual int32_t getKeyCodeState(uint32_t, int32_t keyCode) {
@@ -1033,9 +1101,7 @@ public:
        }
    }

    void setNextDevice(InputDevice* device) {
        mNextDevice = device;
    }
    void setNextDevice(InputDevice* device) { mNextDevice = device; }

    InputDevice* newDevice(int32_t deviceId, int32_t controllerNumber, const std::string& name,
                           uint32_t classes, const std::string& location = "") {
@@ -1049,7 +1115,8 @@ public:

protected:
    virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
            const InputDeviceIdentifier& identifier, uint32_t classes) {
                                            const InputDeviceIdentifier& identifier,
                                            uint32_t classes) {
        if (mNextDevice) {
            InputDevice* device = mNextDevice;
            mNextDevice = nullptr;
@@ -1281,7 +1348,8 @@ protected:
        mFakeEventHub->finishDeviceScan();
        mReader->loopOnce();
        mReader->loopOnce();
        mFakeEventHub->assertQueueIsEmpty();
        ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
        ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty());
    }

    void disableDevice(int32_t deviceId, InputDevice* device) {
@@ -1316,10 +1384,8 @@ TEST_F(InputReaderTest, GetInputDevices) {
    ASSERT_NO_FATAL_FAILURE(addDevice(2, "ignored",
            0, nullptr)); // no classes so device will be ignored


    std::vector<InputDeviceInfo> inputDevices;
    mReader->getInputDevices(inputDevices);

    ASSERT_EQ(1U, inputDevices.size());
    ASSERT_EQ(1, inputDevices[0].getId());
    ASSERT_STREQ("keyboard", inputDevices[0].getIdentifier().name.c_str());
@@ -1345,7 +1411,7 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
    FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
    device->addMapper(mapper);
    mReader->setNextDevice(device);
    addDevice(deviceId, "fake", deviceClass, nullptr);
    ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));

    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled(nullptr));

@@ -1358,20 +1424,20 @@ TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) {
    disableDevice(deviceId, device);
    mReader->loopOnce();

    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
    ASSERT_EQ(deviceId, resetArgs.deviceId);
    ASSERT_EQ(device->isEnabled(), false);

    disableDevice(deviceId, device);
    mReader->loopOnce();
    mFakeListener->assertNotifyDeviceResetWasNotCalled();
    mFakeListener->assertNotifyConfigurationChangedWasNotCalled();
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled());
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasNotCalled());
    ASSERT_EQ(device->isEnabled(), false);

    enableDevice(deviceId, device);
    mReader->loopOnce();
    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
    ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
    ASSERT_EQ(deviceId, resetArgs.deviceId);
    ASSERT_EQ(device->isEnabled(), true);
@@ -1529,7 +1595,7 @@ TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) {
    FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_KEYBOARD);
    device->addMapper(mapper);
    mReader->setNextDevice(device);
    addDevice(deviceId, "fake", deviceClass, nullptr);
    ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));

    NotifyDeviceResetArgs resetArgs;
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
@@ -1537,19 +1603,19 @@ TEST_F(InputReaderTest, DeviceReset_IncrementsSequenceNumber) {

    disableDevice(deviceId, device);
    mReader->loopOnce();
    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
    ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
    prevSequenceNum = resetArgs.sequenceNum;

    enableDevice(deviceId, device);
    mReader->loopOnce();
    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
    ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
    prevSequenceNum = resetArgs.sequenceNum;

    disableDevice(deviceId, device);
    mReader->loopOnce();
    mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs);
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
    ASSERT_TRUE(prevSequenceNum < resetArgs.sequenceNum);
    prevSequenceNum = resetArgs.sequenceNum;
}
@@ -1577,6 +1643,7 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) {
            DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL);
    mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
    mReader->loopOnce();
    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled());

    // Device should only dispatch to the specified display.
    ASSERT_EQ(deviceId, device->getId());
+79 −40
Original line number Diff line number Diff line
@@ -19,6 +19,15 @@

#include "TestInputListener.h"

namespace {

using std::chrono_literals::operator""ms;

// Timeout for waiting for an expected event
static constexpr std::chrono::duration WAIT_TIMEOUT = 5ms;

} // namespace

namespace android {

// --- TestInputListener ---
@@ -29,87 +38,117 @@ TestInputListener::~TestInputListener() { }

void TestInputListener::assertNotifyConfigurationChangedWasCalled(
        NotifyConfigurationChangedArgs* outEventArgs) {
    ASSERT_FALSE(mNotifyConfigurationChangedArgsQueue.empty())
            << "Expected notifyConfigurationChanged() to have been called.";
    if (outEventArgs) {
        *outEventArgs = *mNotifyConfigurationChangedArgsQueue.begin();
    }
    mNotifyConfigurationChangedArgsQueue.erase(mNotifyConfigurationChangedArgsQueue.begin());
    ASSERT_NO_FATAL_FAILURE(
            assertCalled<NotifyConfigurationChangedArgs>(outEventArgs,
                                                         "Expected notifyConfigurationChanged() "
                                                         "to have been called."));
}

void TestInputListener::assertNotifyConfigurationChangedWasNotCalled() {
    ASSERT_TRUE(mNotifyConfigurationChangedArgsQueue.empty())
            << "Expected notifyConfigurationChanged() to not have been called.";
    ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyConfigurationChangedArgs>(
            "notifyConfigurationChanged() should not be called."));
}

void TestInputListener::assertNotifyDeviceResetWasCalled(
        NotifyDeviceResetArgs* outEventArgs) {
    ASSERT_FALSE(mNotifyDeviceResetArgsQueue.empty())
            << "Expected notifyDeviceReset() to have been called.";
    if (outEventArgs) {
        *outEventArgs = *mNotifyDeviceResetArgsQueue.begin();
    }
    mNotifyDeviceResetArgsQueue.erase(mNotifyDeviceResetArgsQueue.begin());
    ASSERT_NO_FATAL_FAILURE(
            assertCalled<
                    NotifyDeviceResetArgs>(outEventArgs,
                                           "Expected notifyDeviceReset() to have been called."));
}

void TestInputListener::assertNotifyDeviceResetWasNotCalled() {
    ASSERT_TRUE(mNotifyDeviceResetArgsQueue.empty())
            << "Expected notifyDeviceReset() to not have been called.";
    ASSERT_NO_FATAL_FAILURE(
            assertNotCalled<NotifyDeviceResetArgs>("notifyDeviceReset() should not be called."));
}

void TestInputListener::assertNotifyKeyWasCalled(NotifyKeyArgs* outEventArgs) {
    ASSERT_FALSE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to have been called.";
    if (outEventArgs) {
        *outEventArgs = *mNotifyKeyArgsQueue.begin();
    }
    mNotifyKeyArgsQueue.erase(mNotifyKeyArgsQueue.begin());
    ASSERT_NO_FATAL_FAILURE(
            assertCalled<NotifyKeyArgs>(outEventArgs, "Expected notifyKey() to have been called."));
}

void TestInputListener::assertNotifyKeyWasNotCalled() {
    ASSERT_TRUE(mNotifyKeyArgsQueue.empty()) << "Expected notifyKey() to not have been called.";
    ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyKeyArgs>("notifyKey() should not be called."));
}

void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs) {
    ASSERT_FALSE(mNotifyMotionArgsQueue.empty()) << "Expected notifyMotion() to have been called.";
    if (outEventArgs) {
        *outEventArgs = *mNotifyMotionArgsQueue.begin();
    }
    mNotifyMotionArgsQueue.erase(mNotifyMotionArgsQueue.begin());
    ASSERT_NO_FATAL_FAILURE(
            assertCalled<NotifyMotionArgs>(outEventArgs,
                                           "Expected notifyMotion() to have been called."));
}

void TestInputListener::assertNotifyMotionWasNotCalled() {
    ASSERT_TRUE(mNotifyMotionArgsQueue.empty())
            << "Expected notifyMotion() to not have been called.";
    ASSERT_NO_FATAL_FAILURE(
            assertNotCalled<NotifySwitchArgs>("notifySwitch() should not be called."));
}

void TestInputListener::assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs) {
    ASSERT_FALSE(mNotifySwitchArgsQueue.empty())
            << "Expected notifySwitch() to have been called.";
    ASSERT_NO_FATAL_FAILURE(
            assertCalled<NotifySwitchArgs>(outEventArgs,
                                           "Expected notifySwitch() to have been called."));
}

template <class NotifyArgsType>
void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) {
    std::unique_lock<std::mutex> lock(mLock);
    base::ScopedLockAssertion assumeLocked(mLock);

    std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
    if (queue.empty()) {
        const bool eventReceived =
                mCondition.wait_for(lock, WAIT_TIMEOUT,
                                    [&queue]() REQUIRES(mLock) { return !queue.empty(); });
        if (!eventReceived) {
            FAIL() << "Timed out waiting for event: " << message.c_str();
        }
    }
    if (outEventArgs) {
        *outEventArgs = *mNotifySwitchArgsQueue.begin();
        *outEventArgs = *queue.begin();
    }
    queue.erase(queue.begin());
}

template <class NotifyArgsType>
void TestInputListener::assertNotCalled(std::string message) {
    std::unique_lock<std::mutex> lock(mLock);
    base::ScopedLockAssertion assumeLocked(mLock);

    std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
    const bool eventReceived = mCondition.wait_for(lock, WAIT_TIMEOUT, [&queue]() REQUIRES(mLock) {
        return !queue.empty();
    });
    if (eventReceived) {
        FAIL() << "Unexpected event: " << message.c_str();
    }
}
    mNotifySwitchArgsQueue.erase(mNotifySwitchArgsQueue.begin());

template <class NotifyArgsType>
void TestInputListener::notify(const NotifyArgsType* args) {
    std::scoped_lock<std::mutex> lock(mLock);

    std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
    queue.push_back(*args);
    mCondition.notify_all();
}

void TestInputListener::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
    mNotifyConfigurationChangedArgsQueue.push_back(*args);
    notify<NotifyConfigurationChangedArgs>(args);
}

void TestInputListener::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
    mNotifyDeviceResetArgsQueue.push_back(*args);
    notify<NotifyDeviceResetArgs>(args);
}

void TestInputListener::notifyKey(const NotifyKeyArgs* args) {
    mNotifyKeyArgsQueue.push_back(*args);
    notify<NotifyKeyArgs>(args);
}

void TestInputListener::notifyMotion(const NotifyMotionArgs* args) {
    mNotifyMotionArgsQueue.push_back(*args);
    notify<NotifyMotionArgs>(args);
}

void TestInputListener::notifySwitch(const NotifySwitchArgs* args) {
        mNotifySwitchArgsQueue.push_back(*args);
    notify<NotifySwitchArgs>(args);
}


} // namespace android
+25 −12
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#ifndef _UI_TEST_INPUT_LISTENER_H
#define _UI_TEST_INPUT_LISTENER_H

#include <android-base/thread_annotations.h>
#include <gtest/gtest.h>
#include "InputListener.h"

@@ -25,13 +26,6 @@ namespace android {
// --- TestInputListener ---

class TestInputListener : public InputListenerInterface {
private:
    std::vector<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgsQueue;
    std::vector<NotifyDeviceResetArgs> mNotifyDeviceResetArgsQueue;
    std::vector<NotifyKeyArgs> mNotifyKeyArgsQueue;
    std::vector<NotifyMotionArgs> mNotifyMotionArgsQueue;
    std::vector<NotifySwitchArgs> mNotifySwitchArgsQueue;

protected:
    virtual ~TestInputListener();

@@ -58,15 +52,34 @@ public:
    void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr);

private:
    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args);
    template <class NotifyArgsType>
    void assertCalled(NotifyArgsType* outEventArgs, std::string message);

    template <class NotifyArgsType>
    void assertNotCalled(std::string message);

    template <class NotifyArgsType>
    void notify(const NotifyArgsType* args);

    virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;

    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;

    virtual void notifyKey(const NotifyKeyArgs* args) override;

    virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args);
    virtual void notifyMotion(const NotifyMotionArgs* args) override;

    virtual void notifyKey(const NotifyKeyArgs* args);
    virtual void notifySwitch(const NotifySwitchArgs* args) override;

    virtual void notifyMotion(const NotifyMotionArgs* args);
    std::mutex mLock;
    std::condition_variable mCondition;

    virtual void notifySwitch(const NotifySwitchArgs* args);
    std::tuple<std::vector<NotifyConfigurationChangedArgs>, //
               std::vector<NotifyDeviceResetArgs>,          //
               std::vector<NotifyKeyArgs>,                  //
               std::vector<NotifyMotionArgs>,               //
               std::vector<NotifySwitchArgs>>               //
            mQueues GUARDED_BY(mLock);
};

} // namespace android