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

Commit b2546a39 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Refactor EventHubTests to use UinputDevice"

parents 3c28860e e0105c9a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ cc_test {
        "InputClassifierConverter_test.cpp",
        "InputDispatcher_test.cpp",
        "InputReader_test.cpp",
        "UinputDevice.cpp",
    ],
    cflags: [
        "-Wall",
+10 −74
Original line number Diff line number Diff line
@@ -16,7 +16,8 @@

#include "EventHub.h"

#include <android-base/stringprintf.h>
#include "UinputDevice.h"

#include <gtest/gtest.h>
#include <inttypes.h>
#include <linux/uinput.h>
@@ -25,16 +26,16 @@

#define TAG "EventHub_test"

using android::createUinputDevice;
using android::EventHub;
using android::EventHubInterface;
using android::InputDeviceIdentifier;
using android::RawEvent;
using android::sp;
using android::base::StringPrintf;
using android::UinputHomeKey;
using std::chrono_literals::operator""ms;

static constexpr bool DEBUG = false;
static const char* DEVICE_NAME = "EventHub Test Device";

static void dumpEvents(const std::vector<RawEvent>& events) {
    for (const RawEvent& event : events) {
@@ -62,27 +63,26 @@ class EventHubTest : public testing::Test {
protected:
    std::unique_ptr<EventHubInterface> mEventHub;
    // We are only going to emulate a single input device currently.
    android::base::unique_fd mDeviceFd;
    std::unique_ptr<UinputHomeKey> mKeyboard;
    int32_t mDeviceId;

    virtual void SetUp() override {
        mEventHub = std::make_unique<EventHub>();
        consumeInitialDeviceAddedEvents();
        createDevice();
        mKeyboard = createUinputDevice<UinputHomeKey>();
        mDeviceId = waitForDeviceCreation();
    }
    virtual void TearDown() override {
        mDeviceFd.reset();
        mKeyboard.reset();
        waitForDeviceClose(mDeviceId);
    }

    void createDevice();
    /**
     * Return the device id of the created device.
     */
    int32_t waitForDeviceCreation();
    void waitForDeviceClose(int32_t deviceId);
    void consumeInitialDeviceAddedEvents();
    void sendEvent(uint16_t type, uint16_t code, int32_t value);
    std::vector<RawEvent> getEvents(std::chrono::milliseconds timeout = 5ms);
};

@@ -105,48 +105,6 @@ std::vector<RawEvent> EventHubTest::getEvents(std::chrono::milliseconds timeout)
    return events;
}

void EventHubTest::createDevice() {
    mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
    if (mDeviceFd < 0) {
        FAIL() << "Can't open /dev/uinput :" << strerror(errno);
    }

    /**
     * Signal which type of events this input device supports.
     * We will emulate a keyboard here.
     */
    // enable key press/release event
    if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_KEY)) {
        ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno);
    }

    // enable set of KEY events
    if (ioctl(mDeviceFd, UI_SET_KEYBIT, KEY_HOME)) {
        ADD_FAILURE() << "Error in ioctl : UI_SET_KEYBIT : KEY_HOME: " << strerror(errno);
    }

    // enable synchronization event
    if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_SYN)) {
        ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno);
    }

    struct uinput_user_dev keyboard = {};
    strlcpy(keyboard.name, DEVICE_NAME, UINPUT_MAX_NAME_SIZE);
    keyboard.id.bustype = BUS_USB;
    keyboard.id.vendor = 0x01;
    keyboard.id.product = 0x01;
    keyboard.id.version = 1;

    if (write(mDeviceFd, &keyboard, sizeof(keyboard)) < 0) {
        FAIL() << "Could not write uinput_user_dev struct into uinput file descriptor: "
               << strerror(errno);
    }

    if (ioctl(mDeviceFd, UI_DEV_CREATE)) {
        FAIL() << "Error in ioctl : UI_DEV_CREATE: " << strerror(errno);
    }
}

/**
 * Since the test runs on a real platform, there will be existing devices
 * in addition to the test devices being added. Therefore, when EventHub is first created,
@@ -176,7 +134,7 @@ int32_t EventHubTest::waitForDeviceCreation() {
    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_ADDED), deviceAddedEvent.type);
    InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceAddedEvent.deviceId);
    const int32_t deviceId = deviceAddedEvent.deviceId;
    EXPECT_EQ(identifier.name, DEVICE_NAME);
    EXPECT_EQ(identifier.name, mKeyboard->getName());
    const RawEvent& finishedDeviceScanEvent = events[1];
    EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
              finishedDeviceScanEvent.type);
@@ -194,22 +152,6 @@ void EventHubTest::waitForDeviceClose(int32_t deviceId) {
              finishedDeviceScanEvent.type);
}

void EventHubTest::sendEvent(uint16_t type, uint16_t code, int32_t value) {
    struct input_event event = {};
    event.type = type;
    event.code = code;
    event.value = value;
    event.time = {}; // uinput ignores the timestamp

    if (write(mDeviceFd, &event, sizeof(input_event)) < 0) {
        std::string msg = StringPrintf("Could not write event %" PRIu16 " %" PRIu16
                                       " with value %" PRId32 " : %s",
                                       type, code, value, strerror(errno));
        ALOGE("%s", msg.c_str());
        ADD_FAILURE() << msg.c_str();
    }
}

/**
 * Ensure that input_events are generated with monotonic clock.
 * That means input_event should receive a timestamp that is in the future of the time
@@ -218,13 +160,7 @@ void EventHubTest::sendEvent(uint16_t type, uint16_t code, int32_t value) {
 */
TEST_F(EventHubTest, InputEvent_TimestampIsMonotonic) {
    nsecs_t lastEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
    // key press
    sendEvent(EV_KEY, KEY_HOME, 1);
    sendEvent(EV_SYN, SYN_REPORT, 0);

    // key release
    sendEvent(EV_KEY, KEY_HOME, 0);
    sendEvent(EV_SYN, SYN_REPORT, 0);
    ASSERT_NO_FATAL_FAILURE(mKeyboard->pressAndReleaseHomeKey());

    std::vector<RawEvent> events = getEvents();
    ASSERT_EQ(4U, events.size()) << "Expected to receive 2 keys and 2 syncs, total of 4 events";
+130 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.
 */

#include "UinputDevice.h"

#include <android-base/stringprintf.h>

namespace android {

// --- UinputDevice ---

UinputDevice::UinputDevice(const char* name) : mName(name) {}

UinputDevice::~UinputDevice() {
    if (ioctl(mDeviceFd, UI_DEV_DESTROY)) {
        ALOGE("Error while destroying uinput device: %s", strerror(errno));
    }
    mDeviceFd.reset();
}

void UinputDevice::init() {
    mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
    if (mDeviceFd < 0) {
        FAIL() << "Can't open /dev/uinput :" << strerror(errno);
    }

    struct uinput_user_dev device = {};
    strlcpy(device.name, mName, UINPUT_MAX_NAME_SIZE);
    device.id.bustype = BUS_USB;
    device.id.vendor = 0x01;
    device.id.product = 0x01;
    device.id.version = 1;

    // Using EXPECT instead of ASSERT to allow the device creation to continue even when
    // some failures are reported when configuring the device.
    EXPECT_NO_FATAL_FAILURE(configureDevice(mDeviceFd, &device));

    if (write(mDeviceFd, &device, sizeof(device)) < 0) {
        FAIL() << "Could not write uinput_user_dev struct into uinput file descriptor: "
               << strerror(errno);
    }

    if (ioctl(mDeviceFd, UI_DEV_CREATE)) {
        FAIL() << "Error in ioctl : UI_DEV_CREATE: " << strerror(errno);
    }
}

void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) {
    struct input_event event = {};
    event.type = type;
    event.code = code;
    event.value = value;
    event.time = {}; // uinput ignores the timestamp

    if (write(mDeviceFd, &event, sizeof(input_event)) < 0) {
        std::string msg = base::StringPrintf("Could not write event %" PRIu16 " %" PRIu16
                                             " with value %" PRId32 " : %s",
                                             type, code, value, strerror(errno));
        ALOGE("%s", msg.c_str());
        ADD_FAILURE() << msg.c_str();
    }
}

// --- UinputKeyboard ---

UinputKeyboard::UinputKeyboard(std::initializer_list<int> keys)
      : UinputDevice(UinputKeyboard::KEYBOARD_NAME), mKeys(keys.begin(), keys.end()) {}

void UinputKeyboard::configureDevice(int fd, uinput_user_dev* device) {
    // enable key press/release event
    if (ioctl(fd, UI_SET_EVBIT, EV_KEY)) {
        ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno);
    }

    // enable set of KEY events
    std::for_each(mKeys.begin(), mKeys.end(), [fd](int key) {
        if (ioctl(fd, UI_SET_KEYBIT, key)) {
            ADD_FAILURE() << "Error in ioctl : UI_SET_KEYBIT : " << key << " : " << strerror(errno);
        }
    });

    // enable synchronization event
    if (ioctl(fd, UI_SET_EVBIT, EV_SYN)) {
        ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno);
    }
}

void UinputKeyboard::pressKey(int key) {
    if (mKeys.find(key) == mKeys.end()) {
        ADD_FAILURE() << mName << ": Cannot inject key press: Key not found: " << key;
    }
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, key, 1));
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
}

void UinputKeyboard::releaseKey(int key) {
    if (mKeys.find(key) == mKeys.end()) {
        ADD_FAILURE() << mName << ": Cannot inject key release: Key not found: " << key;
    }
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, key, 0));
    EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
}

void UinputKeyboard::pressAndReleaseKey(int key) {
    EXPECT_NO_FATAL_FAILURE(pressKey(key));
    EXPECT_NO_FATAL_FAILURE(releaseKey(key));
}

// --- UinputHomeKey---

UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {}

void UinputHomeKey::pressAndReleaseHomeKey() {
    EXPECT_NO_FATAL_FAILURE(pressAndReleaseKey(KEY_HOME));
}

} // namespace android
+111 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.
 */

#ifndef _UI_TEST_INPUT_UINPUT_INJECTOR_H
#define _UI_TEST_INPUT_UINPUT_INJECTOR_H

#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#include <inttypes.h>
#include <linux/uinput.h>
#include <log/log.h>

#include <memory>

namespace android {

// This is the factory method that must be used to create a UinputDevice.
template <class D, class... Ts>
std::unique_ptr<D> createUinputDevice(Ts... args) {
    // Using `new` to access non-public constructors.
    std::unique_ptr<D> dev(new D(&args...));
    EXPECT_NO_FATAL_FAILURE(dev->init());
    return dev;
}

// --- UinputDevice ---

class UinputDevice {
public:
    virtual ~UinputDevice();

    inline const char* getName() const { return mName; }

    // Subclasses must either provide a public constructor or must be-friend the factory method.
    template <class D, class... Ts>
    friend std::unique_ptr<D> createUinputDevice(Ts... args);

protected:
    const char* mName;

    UinputDevice(const char* name);

    // Signals which types of events this device supports before it is created.
    // This must be overridden by subclasses.
    virtual void configureDevice(int fd, uinput_user_dev* device) = 0;

    void injectEvent(uint16_t type, uint16_t code, int32_t value);

private:
    base::unique_fd mDeviceFd;

    // This is called once by the factory method createUinputDevice().
    void init();
};

// --- UinputKeyboard ---

class UinputKeyboard : public UinputDevice {
public:
    static constexpr const char* KEYBOARD_NAME = "Test Keyboard Device";

    // Injects key press and sync.
    void pressKey(int key);
    // Injects key release and sync.
    void releaseKey(int key);
    // Injects 4 events: key press, sync, key release, and sync.
    void pressAndReleaseKey(int key);

    template <class D, class... Ts>
    friend std::unique_ptr<D> createUinputDevice(Ts... args);

protected:
    UinputKeyboard(std::initializer_list<int> keys = {});

private:
    void configureDevice(int fd, uinput_user_dev* device) override;

    std::set<int> mKeys;
};

// --- UinputHomeKey---

// A keyboard device that has a single HOME key.
class UinputHomeKey : public UinputKeyboard {
public:
    // Injects 4 events: key press, sync, key release, and sync.
    void pressAndReleaseHomeKey();

    template <class D, class... Ts>
    friend std::unique_ptr<D> createUinputDevice(Ts... args);

private:
    UinputHomeKey();
};

} // namespace android

#endif // _UI_TEST_INPUT_UINPUT_INJECTOR_H