blob: c69dbefa728cf31e715f20ea2e94eb9eb8120ec0 [file] [log] [blame]
#include <android-base/unique_fd.h>
#include <linux/uinput.h>
#include <utils/String8.h>
#include <cstdint>
#include <memory>
#include <unordered_set>
namespace android {
namespace dvr {
// Simulated evdev input device.
class EvdevInjector {
// EvdevInjector-specific error codes are negative integers; other non-zero
// values returned from public routines are |errno| codes from underlying I/O.
// EvdevInjector maintains a 'sticky' error state, similar to |errno|, so that
// a caller can perform a sequence of operations and check for errors at the
// end using |GetError()|. In general, the first such error will be recorded
// and will suppress effects of further device operations until |ResetError()|
// is called.
enum : int {
ERROR_DEVICE_NAME = -1, // Invalid device name.
ERROR_PROPERTY_RANGE = -2, // |INPUT_PROP_*| code out of range.
ERROR_KEY_RANGE = -3, // |KEY_*|/|BTN_*| code out of range.
ERROR_ABS_RANGE = -4, // |ABS_*| code out of range.
ERROR_SEQUENCING = -5, // Configure/Send out of order.
// Key event |value| is not defined in <linux/input.h>.
enum : int32_t { KEY_RELEASE = 0, KEY_PRESS = 1, KEY_REPEAT = 2 };
// UInput provides a shim to intercept /dev/uinput operations
// just above the system call level, for testing.
class UInput {
UInput() {}
virtual ~UInput() {}
virtual int Open();
virtual int Close();
virtual int Write(const void* buf, size_t count);
virtual int IoctlVoid(int request);
virtual int IoctlSetInt(int request, int value);
base::unique_fd fd_;
EvdevInjector() {}
~EvdevInjector() { Close(); }
void Close();
int GetError() const { return error_; }
void ResetError() { error_ = 0; }
// Configuration must be performed before sending any events.
// |ConfigureBegin()| must be called first, and |ConfigureEnd()| last,
// with zero or more other |Configure...()| calls in between in any order.
// Configure the basic evdev device properties; must be called first.
int ConfigureBegin(const char* device_name, int16_t bustype, int16_t vendor,
int16_t product, int16_t version);
// Configure an optional input device property.
// @param property One of the |INPUT_PROP_*| constants from <linux/input.h>.
int ConfigureInputProperty(int property);
// Configure an input key.
// @param key One of the |KEY_*| or |BTN_*| constants from <linux/input.h>.
int ConfigureKey(uint16_t key);
// Configure an absolute axis.
// @param abs_type One of the |KEY_*| or |BTN_*| constants from
// <linux/input.h>.
int ConfigureAbs(uint16_t abs_type, int32_t min, int32_t max, int32_t fuzz,
int32_t flat);
// Configure the number of multitouch slots.
int ConfigureAbsSlots(int slots);
// Configure multitouch coordinate range.
int ConfigureMultiTouchXY(int32_t x0, int32_t y0, int32_t x1, int32_t y1);
// Complete configuration and create the input device.
int ConfigureEnd();
// Send various events.
int Send(uint16_t type, uint16_t code, int32_t value);
int SendSynReport();
int SendKey(uint16_t code, int32_t value);
int SendAbs(uint16_t code, int32_t value);
int SendMultiTouchSlot(int32_t slot);
int SendMultiTouchXY(int32_t slot, int32_t id, int32_t x, int32_t y);
int SendMultiTouchLift(int32_t slot);
void dumpInternal(String8& result);
// Must be called only between construction and ConfigureBegin().
inline void SetUInputForTesting(UInput* uinput) { uinput_ = uinput; }
// Caller must not retain pointer longer than EvdevInjector.
inline const uinput_user_dev* GetUiDevForTesting() const { return &uidev_; }
// Phase to enforce that configuration is complete before events are sent.
enum class State { NEW, CONFIGURING, READY, CLOSED };
// Sets |error_| if it is not already set; returns |code|.
int Error(int code);
// Returns a nonzero error if the injector is not in the required |state|.
int RequireState(State state);
// Configures an event type if necessary.
// @param type One of the |EV_*| constants from <linux/input.h>.
int EnableEventType(uint16_t type);
// Active pointer to owned or testing UInput.
UInput* uinput_ = nullptr;
std::unique_ptr<UInput> owned_uinput_;
State state_ = State::NEW;
int error_ = 0;
uinput_user_dev uidev_;
std::unordered_set<uint16_t> enabled_event_types_;
int32_t latest_slot_ = -1;
EvdevInjector(const EvdevInjector&) = delete;
void operator=(const EvdevInjector&) = delete;
} // namespace dvr
} // namespace android