Initial InputMappers for evdev input HAL.

The previous design of the InputHost wrapper classes made it very
painful to do testing, so this change also reverts to a more classical
C++ pattern for non-copyable objects. The InputHost classes still simply
call through to the input_host_t and callbacks as before.

Updated unittests to use gmock for mocking the InputHost interactions.

Change-Id: I4b70df2c89ed48af77446b8f5b87a4bde94510bf
This commit is contained in:
Tim Kilbourn 2015-05-04 17:26:30 -07:00
parent 61809ac218
commit 4f3145d75f
19 changed files with 801 additions and 520 deletions

View file

@ -375,13 +375,23 @@ typedef enum {
INPUT_USAGE_LED_CONTROLLER_2,
INPUT_USAGE_LED_CONTROLLER_3,
INPUT_USAGE_LED_CONTROLLER_4,
// switches
INPUT_USAGE_SWITCH_UNKNOWN,
INPUT_USAGE_SWITCH_LID,
INPUT_USAGE_SWITCH_KEYPAD_SLIDE,
INPUT_USAGE_SWITCH_HEADPHONE_INSERT,
INPUT_USAGE_SWITCH_MICROPHONE_INSERT,
INPUT_USAGE_SWITCH_LINEOUT_INSERT,
INPUT_USAGE_SWITCH_CAMERA_LENS_COVER,
} input_usage_t;
typedef enum {
typedef enum input_collection_id {
INPUT_COLLECTION_ID_TOUCH,
INPUT_COLLECTION_ID_KEYBOARD,
INPUT_COLLECTION_ID_MOUSE,
INPUT_COLLECTION_ID_TOUCHPAD,
INPUT_COLLECTION_ID_SWITCH,
// etc
} input_collection_id_t;
@ -412,6 +422,11 @@ typedef struct input_host_callbacks {
input_report_definition_t* (*create_input_report_definition)(input_host_t* host);
input_report_definition_t* (*create_output_report_definition)(input_host_t* host);
/**
* Frees the report definition.
*/
void (*free_report_definition)(input_host_t* host, input_report_definition_t* report_def);
/**
* Append the report to the given input device.
*/

View file

@ -21,7 +21,9 @@ LOCAL_SRC_FILES := \
InputHub.cpp \
InputDevice.cpp \
InputDeviceManager.cpp \
InputHost.cpp
InputHost.cpp \
InputMapper.cpp \
SwitchInputMapper.cpp
LOCAL_SHARED_LIBRARIES := \
libhardware_legacy \

View file

@ -37,7 +37,8 @@ static const char kDevInput[] = "/dev/input";
class EvdevModule {
public:
explicit EvdevModule(InputHost inputHost);
// Takes ownership of the InputHostInterface
explicit EvdevModule(InputHostInterface* inputHost);
void init();
void notifyReport(input_report_t* r);
@ -45,7 +46,7 @@ public:
private:
void loop();
InputHost mInputHost;
std::unique_ptr<InputHostInterface> mInputHost;
std::shared_ptr<InputDeviceManager> mDeviceManager;
std::unique_ptr<InputHub> mInputHub;
std::thread mPollThread;
@ -53,9 +54,9 @@ private:
static std::unique_ptr<EvdevModule> gEvdevModule;
EvdevModule::EvdevModule(InputHost inputHost) :
EvdevModule::EvdevModule(InputHostInterface* inputHost) :
mInputHost(inputHost),
mDeviceManager(std::make_shared<InputDeviceManager>(mInputHost)),
mDeviceManager(std::make_shared<InputDeviceManager>(mInputHost.get())),
mInputHub(std::make_unique<InputHub>(mDeviceManager)) {}
void EvdevModule::init() {
@ -97,7 +98,7 @@ static int dummy_open(const hw_module_t __unused *module, const char __unused *i
static void input_init(const input_module_t* module,
input_host_t* host, input_host_callbacks_t cb) {
LOG_ALWAYS_FATAL_IF(strcmp(module->common.id, INPUT_HARDWARE_MODULE_ID) != 0);
InputHost inputHost = {host, cb};
auto inputHost = new InputHost(host, cb);
gEvdevModule = std::make_unique<EvdevModule>(inputHost);
gEvdevModule->init();
}

View file

@ -32,6 +32,8 @@
#include "InputHub.h"
#include "InputDevice.h"
#include "InputMapper.h"
#include "SwitchInputMapper.h"
#define MSC_ANDROID_TIME_SEC 0x6
#define MSC_ANDROID_TIME_USEC 0x7
@ -104,49 +106,83 @@ static bool getBooleanProperty(const InputProperty& prop) {
return value;
}
static void setDeviceClasses(const InputDeviceNode* node, uint32_t* classes) {
// See if this is a keyboard. Ignore everything in the button range except
// for joystick and gamepad buttons which are handled like keyboards for the
// most part.
bool haveKeyboardKeys = node->hasKeyInRange(0, BTN_MISC) ||
node->hasKeyInRange(KEY_OK, KEY_CNT);
bool haveGamepadButtons = node->hasKeyInRange(BTN_MISC, BTN_MOUSE) ||
node->hasKeyInRange(BTN_JOYSTICK, BTN_DIGI);
if (haveKeyboardKeys || haveGamepadButtons) {
*classes |= INPUT_DEVICE_CLASS_KEYBOARD;
}
EvdevDevice::EvdevDevice(InputHostInterface* host, const std::shared_ptr<InputDeviceNode>& node) :
mHost(host), mDeviceNode(node), mDeviceDefinition(mHost->createDeviceDefinition()) {
InputBus bus = getInputBus(node);
mInputId = mHost->createDeviceIdentifier(
node->getName().c_str(),
node->getProductId(),
node->getVendorId(),
bus,
node->getUniqueId().c_str());
createMappers();
configureDevice();
// If we found a need for at least one mapper, register the device with the
// host. If there were no mappers, this device is effectively ignored, as
// the host won't know about it.
if (mMappers.size() > 0) {
mDeviceHandle = mHost->registerDevice(mInputId, mDeviceDefinition);
for (const auto& mapper : mMappers) {
mapper->setDeviceHandle(mDeviceHandle);
}
}
}
void EvdevDevice::createMappers() {
// See if this is a cursor device such as a trackball or mouse.
if (node->hasKey(BTN_MOUSE)
&& node->hasRelativeAxis(REL_X)
&& node->hasRelativeAxis(REL_Y)) {
*classes |= INPUT_DEVICE_CLASS_CURSOR;
if (mDeviceNode->hasKey(BTN_MOUSE)
&& mDeviceNode->hasRelativeAxis(REL_X)
&& mDeviceNode->hasRelativeAxis(REL_Y)) {
mClasses |= INPUT_DEVICE_CLASS_CURSOR;
//mMappers.push_back(std::make_unique<CursorInputMapper>());
}
// See if this is a touch pad.
bool isStylus = false;
bool haveGamepadButtons = mDeviceNode->hasKeyInRange(BTN_MISC, BTN_MOUSE) ||
mDeviceNode->hasKeyInRange(BTN_JOYSTICK, BTN_DIGI);
// See if this is a touch pad or stylus.
// Is this a new modern multi-touch driver?
if (node->hasAbsoluteAxis(ABS_MT_POSITION_X)
&& node->hasAbsoluteAxis(ABS_MT_POSITION_Y)) {
if (mDeviceNode->hasAbsoluteAxis(ABS_MT_POSITION_X)
&& mDeviceNode->hasAbsoluteAxis(ABS_MT_POSITION_Y)) {
// Some joysticks such as the PS3 controller report axes that conflict
// with the ABS_MT range. Try to confirm that the device really is a
// touch screen.
if (node->hasKey(BTN_TOUCH) || !haveGamepadButtons) {
*classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
if (mDeviceNode->hasKey(BTN_TOUCH) || !haveGamepadButtons) {
mClasses |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
//mMappers.push_back(std::make_unique<MultiTouchInputMapper>());
}
// Is this an old style single-touch driver?
} else if (node->hasKey(BTN_TOUCH)
&& node->hasAbsoluteAxis(ABS_X)
&& node->hasAbsoluteAxis(ABS_Y)) {
*classes != INPUT_DEVICE_CLASS_TOUCH;
} else if (mDeviceNode->hasKey(BTN_TOUCH)
&& mDeviceNode->hasAbsoluteAxis(ABS_X)
&& mDeviceNode->hasAbsoluteAxis(ABS_Y)) {
mClasses |= INPUT_DEVICE_CLASS_TOUCH;
//mMappers.push_back(std::make_unique<SingleTouchInputMapper>());
// Is this a BT stylus?
} else if ((node->hasAbsoluteAxis(ABS_PRESSURE) || node->hasKey(BTN_TOUCH))
&& !node->hasAbsoluteAxis(ABS_X) && !node->hasAbsoluteAxis(ABS_Y)) {
*classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
// Keyboard will try to claim some of the buttons but we really want to
// reserve those so we can fuse it with the touch screen data, so just
// take them back. Note this means an external stylus cannot also be a
// keyboard device.
*classes &= ~INPUT_DEVICE_CLASS_KEYBOARD;
} else if ((mDeviceNode->hasAbsoluteAxis(ABS_PRESSURE) || mDeviceNode->hasKey(BTN_TOUCH))
&& !mDeviceNode->hasAbsoluteAxis(ABS_X) && !mDeviceNode->hasAbsoluteAxis(ABS_Y)) {
mClasses |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
//mMappers.push_back(std::make_unique<ExternalStylusInputMapper>());
isStylus = true;
mClasses &= ~INPUT_DEVICE_CLASS_KEYBOARD;
}
// See if this is a keyboard. Ignore everything in the button range except
// for joystick and gamepad buttons which are handled like keyboards for the
// most part.
// Keyboard will try to claim some of the stylus buttons but we really want
// to reserve those so we can fuse it with the touch screen data. Note this
// means an external stylus cannot also be a keyboard device.
if (!isStylus) {
bool haveKeyboardKeys = mDeviceNode->hasKeyInRange(0, BTN_MISC) ||
mDeviceNode->hasKeyInRange(KEY_OK, KEY_CNT);
if (haveKeyboardKeys || haveGamepadButtons) {
mClasses |= INPUT_DEVICE_CLASS_KEYBOARD;
//mMappers.push_back(std::make_unique<KeyboardInputMapper>());
}
}
// See if this device is a joystick.
@ -154,11 +190,12 @@ static void setDeviceClasses(const InputDeviceNode* node, uint32_t* classes) {
// distinguish them from other devices such as accelerometers that also have
// absolute axes.
if (haveGamepadButtons) {
uint32_t assumedClasses = *classes | INPUT_DEVICE_CLASS_JOYSTICK;
uint32_t assumedClasses = mClasses | INPUT_DEVICE_CLASS_JOYSTICK;
for (int i = 0; i < ABS_CNT; ++i) {
if (node->hasAbsoluteAxis(i)
if (mDeviceNode->hasAbsoluteAxis(i)
&& getAbsAxisUsage(i, assumedClasses) == INPUT_DEVICE_CLASS_JOYSTICK) {
*classes = assumedClasses;
mClasses = assumedClasses;
//mMappers.push_back(std::make_unique<JoystickInputMapper>());
break;
}
}
@ -166,36 +203,40 @@ static void setDeviceClasses(const InputDeviceNode* node, uint32_t* classes) {
// Check whether this device has switches.
for (int i = 0; i < SW_CNT; ++i) {
if (node->hasSwitch(i)) {
*classes |= INPUT_DEVICE_CLASS_SWITCH;
if (mDeviceNode->hasSwitch(i)) {
mClasses |= INPUT_DEVICE_CLASS_SWITCH;
mMappers.push_back(std::make_unique<SwitchInputMapper>());
break;
}
}
// Check whether this device supports the vibrator.
if (node->hasForceFeedback(FF_RUMBLE)) {
*classes |= INPUT_DEVICE_CLASS_VIBRATOR;
// TODO: decide if this is necessary.
if (mDeviceNode->hasForceFeedback(FF_RUMBLE)) {
mClasses |= INPUT_DEVICE_CLASS_VIBRATOR;
//mMappers.push_back(std::make_unique<VibratorInputMapper>());
}
// If the device isn't recognized as something we handle, don't monitor it.
// TODO
ALOGD("device %s classes=0x%x", node->getPath().c_str(), *classes);
ALOGD("device %s classes=0x%x %d mappers", mDeviceNode->getPath().c_str(), mClasses,
mMappers.size());
}
EvdevDevice::EvdevDevice(InputHost host, const std::shared_ptr<InputDeviceNode>& node) :
mHost(host), mDeviceNode(node) {
void EvdevDevice::configureDevice() {
for (const auto& mapper : mMappers) {
auto reportDef = mHost->createInputReportDefinition();
if (mapper->configureInputReport(mDeviceNode.get(), reportDef)) {
mDeviceDefinition->addReport(reportDef);
} else {
mHost->freeReportDefinition(reportDef);
}
InputBus bus = getInputBus(node);
mInputId = mHost.createDeviceIdentifier(
node->getName().c_str(),
node->getProductId(),
node->getVendorId(),
bus,
node->getUniqueId().c_str());
InputPropertyMap propMap = mHost.getDevicePropertyMap(mInputId);
setDeviceClasses(mDeviceNode.get(), &mClasses);
reportDef = mHost->createOutputReportDefinition();
if (mapper->configureOutputReport(mDeviceNode.get(), reportDef)) {
mDeviceDefinition->addReport(reportDef);
} else {
mHost->freeReportDefinition(reportDef);
}
}
}
void EvdevDevice::processInput(InputEvent& event, nsecs_t currentTime) {
@ -260,6 +301,10 @@ void EvdevDevice::processInput(InputEvent& event, nsecs_t currentTime) {
", call time %" PRId64 ".", event.when, time, currentTime);
}
}
for (size_t i = 0; i < mMappers.size(); ++i) {
mMappers[i]->process(event);
}
}
} // namespace android

View file

@ -18,11 +18,13 @@
#define ANDROID_INPUT_DEVICE_H_
#include <memory>
#include <vector>
#include <utils/Timers.h>
#include "InputHost.h"
#include "InputHub.h"
#include "InputMapper.h"
namespace android {
@ -45,16 +47,22 @@ protected:
*/
class EvdevDevice : public InputDeviceInterface {
public:
EvdevDevice(InputHost host, const std::shared_ptr<InputDeviceNode>& node);
EvdevDevice(InputHostInterface* host, const std::shared_ptr<InputDeviceNode>& node);
virtual ~EvdevDevice() override = default;
virtual void processInput(InputEvent& event, nsecs_t currentTime) override;
virtual uint32_t getInputClasses() override { return mClasses; }
private:
InputHost mHost;
void createMappers();
void configureDevice();
InputHostInterface* mHost = nullptr;
std::shared_ptr<InputDeviceNode> mDeviceNode;
InputDeviceIdentifier mInputId;
InputDeviceIdentifier* mInputId = nullptr;
InputDeviceDefinition* mDeviceDefinition = nullptr;
InputDeviceHandle* mDeviceHandle = nullptr;
std::vector<std::unique_ptr<InputMapper>> mMappers;
uint32_t mClasses = 0;
int32_t mOverrideSec = 0;

View file

@ -43,7 +43,8 @@ void InputDeviceManager::onDeviceRemoved(const std::shared_ptr<InputDeviceNode>&
return;
}
// TODO: tell the InputDevice and InputDeviceNode that they are being
// removed so they can run any cleanup.
// removed so they can run any cleanup, including unregistering from the
// host.
mDevices.erase(node);
}

View file

@ -35,7 +35,7 @@ namespace android {
*/
class InputDeviceManager : public InputCallbackInterface {
public:
explicit InputDeviceManager(InputHost host) :
explicit InputDeviceManager(InputHostInterface* host) :
mHost(host) {}
virtual ~InputDeviceManager() override = default;
@ -45,7 +45,7 @@ public:
virtual void onDeviceRemoved(const std::shared_ptr<InputDeviceNode>& node) override;
private:
InputHost mHost;
InputHostInterface* mHost;
template<class T, class U>
using DeviceMap = std::unordered_map<std::shared_ptr<T>, std::shared_ptr<U>>;

View file

@ -18,7 +18,17 @@
namespace android {
void InputReport::reportEvent(InputDeviceHandle d) {
void InputReport::setIntUsage(InputCollectionId id, InputUsage usage, int32_t value,
int32_t arityIndex) {
mCallbacks.input_report_set_usage_int(mHost, mReport, id, usage, value, arityIndex);
}
void InputReport::setBoolUsage(InputCollectionId id, InputUsage usage, bool value,
int32_t arityIndex) {
mCallbacks.input_report_set_usage_bool(mHost, mReport, id, usage, value, arityIndex);
}
void InputReport::reportEvent(InputDeviceHandle* d) {
mCallbacks.report_event(mHost, d, mReport);
}
@ -32,28 +42,19 @@ void InputReportDefinition::declareUsage(InputCollectionId id, InputUsage usage,
id, usage, min, max, resolution);
}
void InputReportDefinition::declareUsage(InputCollectionId id, InputUsage* usage,
void InputReportDefinition::declareUsages(InputCollectionId id, InputUsage* usage,
size_t usageCount) {
mCallbacks.input_report_definition_declare_usages_bool(mHost, mReportDefinition,
id, usage, usageCount);
}
InputReport InputReportDefinition::allocateReport() {
return InputReport(mHost, mCallbacks,
InputReport* InputReportDefinition::allocateReport() {
return new InputReport(mHost, mCallbacks,
mCallbacks.input_allocate_report(mHost, mReportDefinition));
}
void InputDeviceDefinition::addReport(InputReportDefinition r) {
mCallbacks.input_device_definition_add_report(mHost, mDeviceDefinition, r);
}
InputProperty::~InputProperty() {
mCallbacks.input_free_device_property(mHost, mProperty);
}
InputProperty::InputProperty(InputProperty&& rhs) :
InputHostBase(rhs), mProperty(std::move(rhs.mProperty)) {
rhs.mProperty = nullptr;
void InputDeviceDefinition::addReport(InputReportDefinition* r) {
mCallbacks.input_device_definition_add_report(mHost, mDeviceDefinition, *r);
}
const char* InputProperty::getKey() const {
@ -64,51 +65,55 @@ const char* InputProperty::getValue() const {
return mCallbacks.input_get_property_value(mHost, mProperty);
}
InputPropertyMap::~InputPropertyMap() {
mCallbacks.input_free_device_property_map(mHost, mMap);
}
InputPropertyMap::InputPropertyMap(InputPropertyMap&& rhs) :
InputHostBase(rhs), mMap(std::move(rhs.mMap)) {
rhs.mMap = nullptr;
}
InputProperty InputPropertyMap::getDeviceProperty(const char* key) const {
return InputProperty(mHost, mCallbacks,
InputProperty* InputPropertyMap::getDeviceProperty(const char* key) const {
return new InputProperty(mHost, mCallbacks,
mCallbacks.input_get_device_property(mHost, mMap, key));
}
InputDeviceIdentifier InputHost::createDeviceIdentifier(const char* name, int32_t productId,
void InputPropertyMap::freeDeviceProperty(InputProperty* property) const {
mCallbacks.input_free_device_property(mHost, *property);
}
InputDeviceIdentifier* InputHost::createDeviceIdentifier(const char* name, int32_t productId,
int32_t vendorId, InputBus bus, const char* uniqueId) {
return mCallbacks.create_device_identifier(mHost, name, productId, vendorId, bus, uniqueId);
return mCallbacks.create_device_identifier(
mHost, name, productId, vendorId, bus, uniqueId);
}
InputDeviceDefinition InputHost::createDeviceDefinition() {
return InputDeviceDefinition(mHost, mCallbacks, mCallbacks.create_device_definition(mHost));
InputDeviceDefinition* InputHost::createDeviceDefinition() {
return new InputDeviceDefinition(mHost, mCallbacks, mCallbacks.create_device_definition(mHost));
}
InputReportDefinition InputHost::createInputReportDefinition() {
return InputReportDefinition(mHost, mCallbacks,
InputReportDefinition* InputHost::createInputReportDefinition() {
return new InputReportDefinition(mHost, mCallbacks,
mCallbacks.create_input_report_definition(mHost));
}
InputReportDefinition InputHost::createOutputReportDefinition() {
return InputReportDefinition(mHost, mCallbacks,
InputReportDefinition* InputHost::createOutputReportDefinition() {
return new InputReportDefinition(mHost, mCallbacks,
mCallbacks.create_output_report_definition(mHost));
}
InputDeviceHandle InputHost::registerDevice(InputDeviceIdentifier id,
InputDeviceDefinition d) {
return mCallbacks.register_device(mHost, id, d);
void InputHost::freeReportDefinition(InputReportDefinition* reportDef) {
mCallbacks.free_report_definition(mHost, *reportDef);
}
void InputHost::unregisterDevice(InputDeviceHandle handle) {
return mCallbacks.unregister_device(mHost, handle);
InputDeviceHandle* InputHost::registerDevice(InputDeviceIdentifier* id,
InputDeviceDefinition* d) {
return mCallbacks.register_device(mHost, id, *d);
}
InputPropertyMap InputHost::getDevicePropertyMap(InputDeviceIdentifier id) {
return InputPropertyMap(mHost, mCallbacks,
void InputHost::unregisterDevice(InputDeviceHandle* handle) {
mCallbacks.unregister_device(mHost, handle);
}
InputPropertyMap* InputHost::getDevicePropertyMap(InputDeviceIdentifier* id) {
return new InputPropertyMap(mHost, mCallbacks,
mCallbacks.input_get_device_property_map(mHost, id));
}
void InputHost::freeDevicePropertyMap(InputPropertyMap* propertyMap) {
mCallbacks.input_free_device_property_map(mHost, *propertyMap);
}
} // namespace android

View file

@ -25,17 +25,17 @@ namespace android {
/**
* Classes in this file wrap the corresponding interfaces in the Input HAL. They
* are intended to be lightweight and passed by value. It is still important not
* to use an object after a HAL-specific method has freed the underlying
* representation.
* are intended to be lightweight, as they primarily wrap pointers to callbacks.
* It is still important not to use an object after a HAL-specific method has
* freed the underlying representation.
*
* See hardware/input.h for details about each of these methods.
*/
using InputBus = input_bus_t;
using InputCollectionId = input_collection_id_t;
using InputDeviceHandle = input_device_handle_t*;
using InputDeviceIdentifier = input_device_identifier_t*;
using InputDeviceHandle = input_device_handle_t;
using InputDeviceIdentifier = input_device_identifier_t;
using InputUsage = input_usage_t;
class InputHostBase {
@ -43,8 +43,8 @@ protected:
InputHostBase(input_host_t* host, input_host_callbacks_t cb) : mHost(host), mCallbacks(cb) {}
virtual ~InputHostBase() = default;
InputHostBase(const InputHostBase& rhs) = default;
InputHostBase(InputHostBase&& rhs) = default;
InputHostBase(const InputHostBase& rhs) = delete;
InputHostBase(InputHostBase&& rhs) = delete;
input_host_t* mHost;
input_host_callbacks_t mCallbacks;
@ -52,140 +52,139 @@ protected:
class InputReport : private InputHostBase {
public:
virtual ~InputReport() = default;
InputReport(const InputReport& rhs) = default;
InputReport& operator=(const InputReport& rhs) = default;
operator input_report_t*() const { return mReport; }
void reportEvent(InputDeviceHandle d);
private:
friend class InputReportDefinition;
InputReport(input_host_t* host, input_host_callbacks_t cb, input_report_t* r) :
InputHostBase(host, cb), mReport(r) {}
virtual ~InputReport() = default;
virtual void setIntUsage(InputCollectionId id, InputUsage usage, int32_t value,
int32_t arityIndex);
virtual void setBoolUsage(InputCollectionId id, InputUsage usage, bool value,
int32_t arityIndex);
virtual void reportEvent(InputDeviceHandle* d);
operator input_report_t*() const { return mReport; }
InputReport(const InputReport& rhs) = delete;
InputReport& operator=(const InputReport& rhs) = delete;
private:
input_report_t* mReport;
};
class InputReportDefinition : private InputHostBase {
public:
InputReportDefinition(input_host_t* host, input_host_callbacks_t cb,
input_report_definition_t* r) : InputHostBase(host, cb), mReportDefinition(r) {}
virtual ~InputReportDefinition() = default;
InputReportDefinition(const InputReportDefinition& rhs) = default;
InputReportDefinition& operator=(const InputReportDefinition& rhs) = default;
virtual void addCollection(InputCollectionId id, int32_t arity);
virtual void declareUsage(InputCollectionId id, InputUsage usage, int32_t min, int32_t max,
float resolution);
virtual void declareUsages(InputCollectionId id, InputUsage* usage, size_t usageCount);
virtual InputReport* allocateReport();
operator input_report_definition_t*() { return mReportDefinition; }
void addCollection(InputCollectionId id, int32_t arity);
void declareUsage(InputCollectionId id, InputUsage usage, int32_t min, int32_t max,
float resolution);
void declareUsage(InputCollectionId id, InputUsage* usage, size_t usageCount);
InputReport allocateReport();
InputReportDefinition(const InputReportDefinition& rhs) = delete;
InputReportDefinition& operator=(const InputReportDefinition& rhs) = delete;
private:
friend class InputHost;
InputReportDefinition(
input_host_t* host, input_host_callbacks_t cb, input_report_definition_t* r) :
InputHostBase(host, cb), mReportDefinition(r) {}
input_report_definition_t* mReportDefinition;
};
class InputDeviceDefinition : private InputHostBase {
public:
InputDeviceDefinition(input_host_t* host, input_host_callbacks_t cb,
input_device_definition_t* d) :
InputHostBase(host, cb), mDeviceDefinition(d) {}
virtual ~InputDeviceDefinition() = default;
InputDeviceDefinition(const InputDeviceDefinition& rhs) = default;
InputDeviceDefinition& operator=(const InputDeviceDefinition& rhs) = default;
virtual void addReport(InputReportDefinition* r);
operator input_device_definition_t*() { return mDeviceDefinition; }
void addReport(InputReportDefinition r);
InputDeviceDefinition(const InputDeviceDefinition& rhs) = delete;
InputDeviceDefinition& operator=(const InputDeviceDefinition& rhs) = delete;
private:
friend class InputHost;
InputDeviceDefinition(
input_host_t* host, input_host_callbacks_t cb, input_device_definition_t* d) :
InputHostBase(host, cb), mDeviceDefinition(d) {}
input_device_definition_t* mDeviceDefinition;
};
class InputProperty : private InputHostBase {
public:
virtual ~InputProperty();
virtual ~InputProperty() = default;
InputProperty(input_host_t* host, input_host_callbacks_t cb, input_property_t* p) :
InputHostBase(host, cb), mProperty(p) {}
virtual const char* getKey() const;
virtual const char* getValue() const;
operator input_property_t*() { return mProperty; }
const char* getKey() const;
const char* getValue() const;
// Transfers ownership of the input_property_t pointer.
InputProperty(InputProperty&& rhs);
// Prevent copy/assign because of the ownership of the underlying
// input_property_t pointer.
InputProperty(const InputProperty& rhs) = delete;
InputProperty& operator=(const InputProperty& rhs) = delete;
private:
friend class InputPropertyMap;
InputProperty(
input_host_t* host, input_host_callbacks_t cb, input_property_t* p) :
InputHostBase(host, cb), mProperty(p) {}
input_property_t* mProperty;
};
class InputPropertyMap : private InputHostBase {
public:
virtual ~InputPropertyMap();
virtual ~InputPropertyMap() = default;
InputPropertyMap(input_host_t* host, input_host_callbacks_t cb, input_property_map_t* m) :
InputHostBase(host, cb), mMap(m) {}
virtual InputProperty* getDeviceProperty(const char* key) const;
virtual void freeDeviceProperty(InputProperty* property) const;
operator input_property_map_t*() { return mMap; }
InputProperty getDeviceProperty(const char* key) const;
// Transfers ownership of the input_property_map_t pointer.
InputPropertyMap(InputPropertyMap&& rhs);
// Prevent copy/assign because of the ownership of the underlying
// input_property_map_t pointer.
InputPropertyMap(const InputPropertyMap& rhs) = delete;
InputPropertyMap& operator=(const InputPropertyMap& rhs) = delete;
private:
friend class InputHost;
InputPropertyMap(
input_host_t* host, input_host_callbacks_t cb, input_property_map_t* m) :
InputHostBase(host, cb), mMap(m) {}
input_property_map_t* mMap;
};
class InputHost : private InputHostBase {
class InputHostInterface {
public:
virtual ~InputHostInterface() = default;
virtual InputDeviceIdentifier* createDeviceIdentifier(const char* name, int32_t productId,
int32_t vendorId, InputBus bus, const char* uniqueId) = 0;
virtual InputDeviceDefinition* createDeviceDefinition() = 0;
virtual InputReportDefinition* createInputReportDefinition() = 0;
virtual InputReportDefinition* createOutputReportDefinition() = 0;
virtual void freeReportDefinition(InputReportDefinition* reportDef) = 0;
virtual InputDeviceHandle* registerDevice(InputDeviceIdentifier* id,
InputDeviceDefinition* d) = 0;
virtual void unregisterDevice(InputDeviceHandle* handle) = 0;
virtual InputPropertyMap* getDevicePropertyMap(InputDeviceIdentifier* id) = 0;
virtual void freeDevicePropertyMap(InputPropertyMap* propertyMap) = 0;
};
class InputHost : public InputHostInterface, private InputHostBase {
public:
InputHost(input_host_t* host, input_host_callbacks_t cb) : InputHostBase(host, cb) {}
virtual ~InputHost() = default;
InputHost(const InputHost& rhs) = default;
InputHost& operator=(const InputHost& rhs) = default;
InputDeviceIdentifier* createDeviceIdentifier(const char* name, int32_t productId,
int32_t vendorId, InputBus bus, const char* uniqueId) override;
InputDeviceIdentifier createDeviceIdentifier(const char* name, int32_t productId,
int32_t vendorId, InputBus bus, const char* uniqueId);
InputDeviceDefinition* createDeviceDefinition() override;
InputReportDefinition* createInputReportDefinition() override;
InputReportDefinition* createOutputReportDefinition() override;
virtual void freeReportDefinition(InputReportDefinition* reportDef) override;
InputDeviceDefinition createDeviceDefinition();
InputReportDefinition createInputReportDefinition();
InputReportDefinition createOutputReportDefinition();
InputDeviceHandle* registerDevice(InputDeviceIdentifier* id, InputDeviceDefinition* d) override;
void unregisterDevice(InputDeviceHandle* handle) override;
InputDeviceHandle registerDevice(InputDeviceIdentifier id, InputDeviceDefinition d);
void unregisterDevice(InputDeviceHandle handle);
InputPropertyMap* getDevicePropertyMap(InputDeviceIdentifier* id) override;
void freeDevicePropertyMap(InputPropertyMap* propertyMap) override;
InputPropertyMap getDevicePropertyMap(InputDeviceIdentifier id);
InputHost(const InputHost& rhs) = delete;
InputHost& operator=(const InputHost& rhs) = delete;
};
} // namespace android

View file

@ -0,0 +1,28 @@
/*
* Copyright (C) 2015 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 "InputMapper.h"
namespace android {
InputReport* InputMapper::getInputReport() {
if (mReport) return mReport;
if (mInputReportDef == nullptr) return nullptr;
mReport = mInputReportDef->allocateReport();
return mReport;
}
} // namespace android

View file

@ -0,0 +1,82 @@
/*
* Copyright (C) 2015 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 ANDROID_INPUT_MAPPER_H_
#define ANDROID_INPUT_MAPPER_H_
#include "InputHost.h"
#include "InputHub.h"
namespace android {
/**
* An InputMapper processes raw evdev input events and combines them into
* Android input HAL reports. A given InputMapper will focus on a particular
* type of input, like key presses or touch events. A single InputDevice may
* have multiple InputMappers, corresponding to the different types of inputs it
* supports.
*/
class InputMapper {
public:
virtual ~InputMapper() = default;
/**
* If the mapper supports input events from the InputDevice,
* configureInputReport will populate the InputReportDefinition and return
* true. If input is not supported, false is returned, and the InputDevice
* may free or re-use the InputReportDefinition.
*/
virtual bool configureInputReport(InputDeviceNode* devNode, InputReportDefinition* report) {
return false;
}
/**
* If the mapper supports output events from the InputDevice,
* configureOutputReport will populate the InputReportDefinition and return
* true. If output is not supported, false is returned, and the InputDevice
* may free or re-use the InputReportDefinition.
*/
virtual bool configureOutputReport(InputDeviceNode* devNode, InputReportDefinition* report) {
return false;
}
// Set the InputDeviceHandle after registering the device with the host.
virtual void setDeviceHandle(InputDeviceHandle* handle) { mDeviceHandle = handle; }
// Process the InputEvent.
virtual void process(const InputEvent& event) = 0;
protected:
virtual void setInputReportDefinition(InputReportDefinition* reportDef) final {
mInputReportDef = reportDef;
}
virtual void setOutputReportDefinition(InputReportDefinition* reportDef) final {
mOutputReportDef = reportDef;
}
virtual InputReportDefinition* getInputReportDefinition() final { return mInputReportDef; }
virtual InputReportDefinition* getOutputReportDefinition() final { return mOutputReportDef; }
virtual InputDeviceHandle* getDeviceHandle() final { return mDeviceHandle; }
virtual InputReport* getInputReport() final;
private:
InputReportDefinition* mInputReportDef = nullptr;
InputReportDefinition* mOutputReportDef = nullptr;
InputDeviceHandle* mDeviceHandle = nullptr;
InputReport* mReport = nullptr;
};
} // namespace android
#endif // ANDROID_INPUT_MAPPER_H_

View file

@ -0,0 +1,123 @@
/*
* Copyright (C) 2015 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.
*/
#define LOG_TAG "SwitchInputMapper"
//#define LOG_NDEBUG 0
#include "SwitchInputMapper.h"
#include <linux/input.h>
#include <hardware/input.h>
#include <utils/Log.h>
#include "InputHost.h"
#include "InputHub.h"
namespace android {
static struct {
int32_t scancode;
InputUsage usage;
} codeMap[] = {
{SW_LID, INPUT_USAGE_SWITCH_LID},
{SW_TABLET_MODE, INPUT_USAGE_SWITCH_UNKNOWN},
{SW_HEADPHONE_INSERT, INPUT_USAGE_SWITCH_HEADPHONE_INSERT},
{SW_RFKILL_ALL, INPUT_USAGE_SWITCH_UNKNOWN},
{SW_MICROPHONE_INSERT, INPUT_USAGE_SWITCH_MICROPHONE_INSERT},
{SW_DOCK, INPUT_USAGE_SWITCH_UNKNOWN},
{SW_LINEOUT_INSERT, INPUT_USAGE_SWITCH_LINEOUT_INSERT},
{SW_JACK_PHYSICAL_INSERT, INPUT_USAGE_SWITCH_UNKNOWN},
{SW_VIDEOOUT_INSERT, INPUT_USAGE_SWITCH_UNKNOWN},
{SW_CAMERA_LENS_COVER, INPUT_USAGE_SWITCH_CAMERA_LENS_COVER},
{SW_KEYPAD_SLIDE, INPUT_USAGE_SWITCH_KEYPAD_SLIDE},
{SW_FRONT_PROXIMITY, INPUT_USAGE_SWITCH_UNKNOWN},
{SW_ROTATE_LOCK, INPUT_USAGE_SWITCH_UNKNOWN},
{SW_LINEIN_INSERT, INPUT_USAGE_SWITCH_UNKNOWN},
{0x0e /* unused */, INPUT_USAGE_SWITCH_UNKNOWN},
{SW_MAX, INPUT_USAGE_SWITCH_UNKNOWN},
};
SwitchInputMapper::SwitchInputMapper()
: InputMapper() {
static_assert(SW_CNT <= 32, "More than 32 switches defined in linux/input.h");
}
bool SwitchInputMapper::configureInputReport(InputDeviceNode* devNode,
InputReportDefinition* report) {
InputUsage usages[SW_CNT];
int numUsages = 0;
for (int32_t i = 0; i < SW_CNT; ++i) {
if (devNode->hasSwitch(codeMap[i].scancode)) {
usages[numUsages++] = codeMap[i].usage;
}
}
if (numUsages == 0) {
ALOGE("SwitchInputMapper found no switches for %s!", devNode->getPath().c_str());
return false;
}
setInputReportDefinition(report);
getInputReportDefinition()->addCollection(INPUT_COLLECTION_ID_SWITCH, 1);
getInputReportDefinition()->declareUsages(INPUT_COLLECTION_ID_SWITCH, usages, numUsages);
return true;
}
void SwitchInputMapper::process(const InputEvent& event) {
ALOGD("processing switch event. type=%d code=%d value=%d",
event.type, event.code, event.value);
switch (event.type) {
case EV_SW:
processSwitch(event.code, event.value);
break;
case EV_SYN:
if (event.code == SYN_REPORT) {
sync(event.when);
}
break;
default:
ALOGD("unknown switch event type: %d", event.type);
}
}
void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) {
if (switchCode >= 0 && switchCode < SW_CNT) {
if (switchValue) {
mSwitchValues.markBit(switchCode);
} else {
mSwitchValues.clearBit(switchCode);
}
mUpdatedSwitchMask.markBit(switchCode);
}
}
void SwitchInputMapper::sync(nsecs_t when) {
if (mUpdatedSwitchMask.isEmpty()) {
// Clear the values just in case.
mSwitchValues.clear();
return;
}
while (!mUpdatedSwitchMask.isEmpty()) {
auto bit = mUpdatedSwitchMask.firstMarkedBit();
getInputReport()->setBoolUsage(INPUT_COLLECTION_ID_SWITCH, codeMap[bit].usage,
mSwitchValues.hasBit(bit), 0);
mUpdatedSwitchMask.clearBit(bit);
}
getInputReport()->reportEvent(getDeviceHandle());
mUpdatedSwitchMask.clear();
mSwitchValues.clear();
}
} // namespace android

View file

@ -0,0 +1,52 @@
/*
* Copyright (C) 2015 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 ANDROID_SWITCH_INPUT_MAPPER_H_
#define ANDROID_SWITCH_INPUT_MAPPER_H_
#include <cstdint>
#include <utils/BitSet.h>
#include <utils/Timers.h>
#include "InputMapper.h"
namespace android {
class InputDeviceNode;
class InputReportDefinition;
struct InputEvent;
class SwitchInputMapper : public InputMapper {
public:
SwitchInputMapper();
virtual ~SwitchInputMapper() = default;
virtual bool configureInputReport(InputDeviceNode* devNode,
InputReportDefinition* report) override;
virtual void process(const InputEvent& event) override;
private:
void processSwitch(int32_t switchCode, int32_t switchValue);
void sync(nsecs_t when);
BitSet32 mSwitchValues;
BitSet32 mUpdatedSwitchMask;
};
} // namespace android
#endif // ANDROID_SWITCH_INPUT_MAPPER_H_

View file

@ -2,13 +2,17 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_C_INCLUDES += hardware/libhardware/modules/input/evdev
LOCAL_C_INCLUDES += $(TOP)/external/gmock/include
LOCAL_SRC_FILES:= \
InputDevice_test.cpp \
InputHub_test.cpp \
InputMocks.cpp \
SwitchInputMapper_test.cpp \
TestHelpers.cpp
LOCAL_STATIC_LIBRARIES := libgmock
LOCAL_SHARED_LIBRARIES := \
libinput_evdev \
liblog \

View file

@ -26,9 +26,9 @@
#include <utils/Timers.h>
#include "InputDevice.h"
#include "InputHost.h"
#include "InputHub.h"
#include "InputMocks.h"
#include "MockInputHost.h"
// # of milliseconds to allow for timing measurements
#define TIMING_TOLERANCE_MS 25
@ -36,26 +36,41 @@
#define MSC_ANDROID_TIME_SEC 0x6
#define MSC_ANDROID_TIME_USEC 0x7
using ::testing::_;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::ReturnNull;
namespace android {
namespace tests {
class EvdevDeviceTest : public ::testing::Test {
protected:
virtual void SetUp() override {
mMockHost.reset(new MockInputHost());
}
virtual void SetUp() {
// Creating device identifiers and definitions should always happen.
EXPECT_CALL(mHost, createDeviceIdentifier(_, _, _, _, _))
.WillOnce(ReturnNull());
EXPECT_CALL(mHost, createDeviceDefinition())
.WillOnce(Return(&mDeviceDef));
// InputMappers may cause any of these to be called, but we are not
// testing these here.
ON_CALL(mHost, createInputReportDefinition())
.WillByDefault(Return(&mReportDef));
ON_CALL(mHost, createOutputReportDefinition())
.WillByDefault(Return(&mReportDef));
ON_CALL(mHost, registerDevice(_, _))
.WillByDefault(ReturnNull());
}
virtual void TearDown() override {
ASSERT_TRUE(mMockHost->checkAllocations());
}
std::unique_ptr<MockInputHost> mMockHost;
MockInputHost mHost;
// Ignore uninteresting calls on the report definitions by using NiceMocks.
NiceMock<MockInputReportDefinition> mReportDef;
NiceMock<MockInputDeviceDefinition> mDeviceDef;
};
TEST_F(EvdevDeviceTest, testOverrideTime) {
InputHost host = {mMockHost.get(), kTestCallbacks};
auto node = std::make_shared<MockInputDeviceNode>();
auto device = std::make_unique<EvdevDevice>(host, node);
auto device = std::make_unique<EvdevDevice>(&mHost, node);
ASSERT_TRUE(device != nullptr);
// Send two timestamp override events before an input event.
@ -83,9 +98,8 @@ TEST_F(EvdevDeviceTest, testOverrideTime) {
}
TEST_F(EvdevDeviceTest, testWrongClockCorrection) {
InputHost host = {mMockHost.get(), kTestCallbacks};
auto node = std::make_shared<MockInputDeviceNode>();
auto device = std::make_unique<EvdevDevice>(host, node);
auto device = std::make_unique<EvdevDevice>(&mHost, node);
ASSERT_TRUE(device != nullptr);
auto now = systemTime(SYSTEM_TIME_MONOTONIC);
@ -100,9 +114,8 @@ TEST_F(EvdevDeviceTest, testWrongClockCorrection) {
}
TEST_F(EvdevDeviceTest, testClockCorrectionOk) {
InputHost host = {mMockHost.get(), kTestCallbacks};
auto node = std::make_shared<MockInputDeviceNode>();
auto device = std::make_unique<EvdevDevice>(host, node);
auto device = std::make_unique<EvdevDevice>(&mHost, node);
ASSERT_TRUE(device != nullptr);
auto now = systemTime(SYSTEM_TIME_MONOTONIC);
@ -118,68 +131,70 @@ TEST_F(EvdevDeviceTest, testClockCorrectionOk) {
}
TEST_F(EvdevDeviceTest, testN7v2Touchscreen) {
InputHost host = {mMockHost.get(), kTestCallbacks};
auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getElanTouchscreen());
auto device = std::make_unique<EvdevDevice>(host, node);
auto device = std::make_unique<EvdevDevice>(&mHost, node);
EXPECT_EQ(INPUT_DEVICE_CLASS_TOUCH|INPUT_DEVICE_CLASS_TOUCH_MT,
device->getInputClasses());
}
TEST_F(EvdevDeviceTest, testN7v2ButtonJack) {
InputHost host = {mMockHost.get(), kTestCallbacks};
auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getButtonJack());
auto device = std::make_unique<EvdevDevice>(host, node);
auto device = std::make_unique<EvdevDevice>(&mHost, node);
EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
}
TEST_F(EvdevDeviceTest, testN7v2HeadsetJack) {
InputHost host = {mMockHost.get(), kTestCallbacks};
// Eventually these mock device tests will all expect these calls. For now
// only the SwitchInputMapper has been implemented.
// TODO: move this expectation out to a common function
EXPECT_CALL(mHost, createInputReportDefinition());
EXPECT_CALL(mHost, registerDevice(_, _));
auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getHeadsetJack());
auto device = std::make_unique<EvdevDevice>(host, node);
auto device = std::make_unique<EvdevDevice>(&mHost, node);
EXPECT_EQ(INPUT_DEVICE_CLASS_SWITCH, device->getInputClasses());
}
TEST_F(EvdevDeviceTest, testN7v2H2wButton) {
InputHost host = {mMockHost.get(), kTestCallbacks};
auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getH2wButton());
auto device = std::make_unique<EvdevDevice>(host, node);
auto device = std::make_unique<EvdevDevice>(&mHost, node);
EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
}
TEST_F(EvdevDeviceTest, testN7v2GpioKeys) {
InputHost host = {mMockHost.get(), kTestCallbacks};
auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getGpioKeys());
auto device = std::make_unique<EvdevDevice>(host, node);
auto device = std::make_unique<EvdevDevice>(&mHost, node);
EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
}
TEST_F(EvdevDeviceTest, testNexusPlayerGpioKeys) {
InputHost host = {mMockHost.get(), kTestCallbacks};
auto node = std::shared_ptr<MockInputDeviceNode>(MockNexusPlayer::getGpioKeys());
auto device = std::make_unique<EvdevDevice>(host, node);
auto device = std::make_unique<EvdevDevice>(&mHost, node);
EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
}
TEST_F(EvdevDeviceTest, testNexusPlayerMidPowerBtn) {
InputHost host = {mMockHost.get(), kTestCallbacks};
auto node = std::shared_ptr<MockInputDeviceNode>(MockNexusPlayer::getMidPowerBtn());
auto device = std::make_unique<EvdevDevice>(host, node);
auto device = std::make_unique<EvdevDevice>(&mHost, node);
EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
}
TEST_F(EvdevDeviceTest, testNexusRemote) {
InputHost host = {mMockHost.get(), kTestCallbacks};
auto node = std::shared_ptr<MockInputDeviceNode>(MockNexusPlayer::getNexusRemote());
auto device = std::make_unique<EvdevDevice>(host, node);
auto device = std::make_unique<EvdevDevice>(&mHost, node);
EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
}
TEST_F(EvdevDeviceTest, testAsusGamepad) {
InputHost host = {mMockHost.get(), kTestCallbacks};
auto node = std::shared_ptr<MockInputDeviceNode>(MockNexusPlayer::getAsusGamepad());
auto device = std::make_unique<EvdevDevice>(host, node);
auto device = std::make_unique<EvdevDevice>(&mHost, node);
EXPECT_EQ(INPUT_DEVICE_CLASS_JOYSTICK|INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
}
TEST_F(EvdevDeviceTest, testMocks) {
auto node = std::make_shared<MockInputDeviceNode>();
auto device = std::make_unique<EvdevDevice>(&mHost, node);
}
} // namespace tests
} // namespace android

View file

@ -1,104 +1,7 @@
#include "InputMocks.h"
// Private test definitions of opaque HAL structs
// Not used
struct input_property_map {};
// Holds the key and value from the mock host's PropertyMap
struct input_property {
android::String8 key;
android::String8 value;
};
namespace android {
bool MockInputHost::checkAllocations() const {
bool ret = true;
if (mMapAllocations != 0) {
ALOGE("Leaked %d device property map allocations", mMapAllocations);
ret = false;
}
for (auto entry : mPropertyAllocations) {
if (entry.second != 0) {
ALOGE("Leaked %d property allocation for %s", entry.second, entry.first.c_str());
ret = false;
}
}
return ret;
}
input_device_identifier_t* MockInputHost::createDeviceIdentifier(
const char* name, int32_t product_id, int32_t vendor_id,
input_bus_t bus, const char* unique_id) {
mDeviceId.reset(new input_device_identifier_t{
.name = name,
.productId = product_id,
.vendorId = vendor_id,
.bus = bus,
.uniqueId = unique_id
});
// Just return the raw pointer. We don't have a method for deallocating
// device identifiers yet, and they should exist throughout the lifetime of
// the input process for now.
return mDeviceId.get();
}
input_property_map_t* MockInputHost::getDevicePropertyMap(input_device_identifier_t* id) {
mMapAllocations++;
// Handled in the MockInputHost.
return nullptr;
}
input_property_t* MockInputHost::getDeviceProperty(input_property_map_t* map, const char* key) {
mPropertyAllocations[key]++;
return new input_property_t{.key = String8(key)};
}
const char* MockInputHost::getPropertyKey(input_property_t* property) {
return property->key.string();
}
const char* MockInputHost::getPropertyValue(input_property_t* property) {
if (!mDevicePropertyMap.tryGetProperty(property->key, property->value)) {
return nullptr;
}
return property->value.string();
}
void MockInputHost::freeDeviceProperty(input_property_t* property) {
if (property != nullptr) {
mPropertyAllocations[property->key.string()]--;
delete property;
}
}
void MockInputHost::freeDevicePropertyMap(input_property_map_t* map) {
mMapAllocations--;
}
input_host_callbacks_t kTestCallbacks = {
.create_device_identifier = create_device_identifier,
.create_device_definition = create_device_definition,
.create_input_report_definition = create_input_report_definition,
.create_output_report_definition = create_output_report_definition,
.input_device_definition_add_report = input_device_definition_add_report,
.input_report_definition_add_collection = input_report_definition_add_collection,
.input_report_definition_declare_usage_int = input_report_definition_declare_usage_int,
.input_report_definition_declare_usages_bool = input_report_definition_declare_usages_bool,
.register_device = register_device,
.input_allocate_report = input_allocate_report,
.input_report_set_usage_int = input_report_set_usage_int,
.input_report_set_usage_bool = input_report_set_usage_bool,
.report_event = report_event,
.input_get_device_property_map = input_get_device_property_map,
.input_get_device_property = input_get_device_property,
.input_get_property_key = input_get_property_key,
.input_get_property_value = input_get_property_value,
.input_free_device_property = input_free_device_property,
.input_free_device_property_map = input_free_device_property_map,
};
bool MockInputDeviceNode::hasKeyInRange(int32_t startKey, int32_t endKey) const {
auto iter = mKeys.lower_bound(startKey);
if (iter == mKeys.end()) return false;
@ -321,86 +224,4 @@ MockInputDeviceNode* getAsusGamepad() {
} // namespace MockNexusPlayer
::input_device_identifier_t* create_device_identifier(input_host_t* host,
const char* name, int32_t product_id, int32_t vendor_id,
input_bus_t bus, const char* unique_id) {
auto mockHost = static_cast<MockInputHost*>(host);
return mockHost->createDeviceIdentifier(name, product_id, vendor_id, bus, unique_id);
}
input_device_definition_t* create_device_definition(input_host_t* host) {
return nullptr;
}
input_report_definition_t* create_input_report_definition(input_host_t* host) {
return nullptr;
}
input_report_definition_t* create_output_report_definition(input_host_t* host) {
return nullptr;
}
void input_device_definition_add_report(input_host_t* host,
input_device_definition_t* d, input_report_definition_t* r) { }
void input_report_definition_add_collection(input_host_t* host,
input_report_definition_t* report, input_collection_id_t id, int32_t arity) { }
void input_report_definition_declare_usage_int(input_host_t* host,
input_report_definition_t* report, input_collection_id_t id,
input_usage_t usage, int32_t min, int32_t max, float resolution) { }
void input_report_definition_declare_usages_bool(input_host_t* host,
input_report_definition_t* report, input_collection_id_t id,
input_usage_t* usage, size_t usage_count) { }
input_device_handle_t* register_device(input_host_t* host,
input_device_identifier_t* id, input_device_definition_t* d) {
return nullptr;
}
input_report_t* input_allocate_report(input_host_t* host, input_report_definition_t* r) {
return nullptr;
}
void input_report_set_usage_int(input_host_t* host, input_report_t* r,
input_collection_id_t id, input_usage_t usage, int32_t value, int32_t arity_index) { }
void input_report_set_usage_bool(input_host_t* host, input_report_t* r,
input_collection_id_t id, input_usage_t usage, bool value, int32_t arity_index) { }
void report_event(input_host_t* host, input_device_handle_t* d, input_report_t* report) { }
input_property_map_t* input_get_device_property_map(input_host_t* host,
input_device_identifier_t* id) {
auto mockHost = static_cast<MockInputHost*>(host);
return mockHost->getDevicePropertyMap(id);
}
input_property_t* input_get_device_property(input_host_t* host, input_property_map_t* map,
const char* key) {
auto mockHost = static_cast<MockInputHost*>(host);
return mockHost->getDeviceProperty(map, key);
}
const char* input_get_property_key(input_host_t* host, input_property_t* property) {
auto mockHost = static_cast<MockInputHost*>(host);
return mockHost->getPropertyKey(property);
}
const char* input_get_property_value(input_host_t* host, input_property_t* property) {
auto mockHost = static_cast<MockInputHost*>(host);
return mockHost->getPropertyValue(property);
}
void input_free_device_property(input_host_t* host, input_property_t* property) {
auto mockHost = static_cast<MockInputHost*>(host);
return mockHost->freeDeviceProperty(property);
}
void input_free_device_property_map(input_host_t* host, input_property_map_t* map) {
auto mockHost = static_cast<MockInputHost*>(host);
return mockHost->freeDevicePropertyMap(map);
}
} // namespace android

View file

@ -18,72 +18,15 @@
#define ANDROID_INPUT_MOCKS_H_
#include <map>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <linux/input.h>
#include <hardware/input.h>
#include <utils/PropertyMap.h>
#include "InputHub.h"
// Test definitions of opaque HAL structs
struct input_host {};
struct input_device_identifier {
const char* name;
const char* uniqueId;
input_bus_t bus;
int32_t vendorId;
int32_t productId;
int32_t version;
};
namespace android {
extern input_host_callbacks_t kTestCallbacks;
class MockInputHost : public ::input_host_t {
public:
virtual ~MockInputHost() = default;
void addDeviceProperty(const std::string& key, const std::string& value) {
mDevicePropertyMap.addProperty(String8(key.c_str()), String8(value.c_str()));
}
/**
* Call this at the end of a test to verify that any allocations made
* during the test were freed.
*/
bool checkAllocations() const;
// Callbacks
input_device_identifier_t* createDeviceIdentifier(
const char* name, int32_t product_id, int32_t vendor_id,
input_bus_t bus, const char* unique_id);
input_property_map_t* getDevicePropertyMap(input_device_identifier_t* id);
input_property_t* getDeviceProperty(input_property_map_t* map, const char* key);
const char* getPropertyKey(input_property_t* property);
const char* getPropertyValue(input_property_t* property);
void freeDeviceProperty(input_property_t* property);
void freeDevicePropertyMap(input_property_map_t* map);
private:
PropertyMap mDevicePropertyMap;
std::unique_ptr<input_device_identifier_t> mDeviceId;
int32_t mMapAllocations = 0;
std::unordered_map<std::string, int32_t> mPropertyAllocations;
};
class MockInputDeviceNode : public InputDeviceNode {
public:
MockInputDeviceNode() = default;
@ -194,63 +137,6 @@ MockInputDeviceNode* getNexusRemote();
MockInputDeviceNode* getAsusGamepad();
} // namespace MockNexusPlayer
// HAL method prototypes used in mock callbacks
extern "C" {
input_device_identifier_t* create_device_identifier(input_host_t* host,
const char* name, int32_t product_id, int32_t vendor_id,
input_bus_t bus, const char* unique_id);
input_device_definition_t* create_device_definition(input_host_t* host);
input_report_definition_t* create_input_report_definition(input_host_t* host);
input_report_definition_t* create_output_report_definition(input_host_t* host);
void input_device_definition_add_report(input_host_t* host,
input_device_definition_t* d, input_report_definition_t* r);
void input_report_definition_add_collection(input_host_t* host,
input_report_definition_t* report, input_collection_id_t id, int32_t arity);
void input_report_definition_declare_usage_int(input_host_t* host,
input_report_definition_t* report, input_collection_id_t id,
input_usage_t usage, int32_t min, int32_t max, float resolution);
void input_report_definition_declare_usages_bool(input_host_t* host,
input_report_definition_t* report, input_collection_id_t id,
input_usage_t* usage, size_t usage_count);
input_device_handle_t* register_device(input_host_t* host,
input_device_identifier_t* id, input_device_definition_t* d);
void unregister_device(input_host_t* host, input_device_handle_t* handle);
input_report_t* input_allocate_report(input_host_t* host, input_report_definition_t* r);
void input_report_set_usage_int(input_host_t* host, input_report_t* r,
input_collection_id_t id, input_usage_t usage, int32_t value, int32_t arity_index);
void input_report_set_usage_bool(input_host_t* host, input_report_t* r,
input_collection_id_t id, input_usage_t usage, bool value, int32_t arity_index);
void report_event(input_host_t* host, input_device_handle_t* d, input_report_t* report);
input_property_map_t* input_get_device_property_map(input_host_t* host,
input_device_identifier_t* id);
input_property_t* input_get_device_property(input_host_t* host, input_property_map_t* map,
const char* key);
const char* input_get_property_key(input_host_t* host, input_property_t* property);
const char* input_get_property_value(input_host_t* host, input_property_t* property);
void input_free_device_property(input_host_t* host, input_property_t* property);
void input_free_device_property_map(input_host_t* host, input_property_map_t* map);
} // extern "C"
} // namespace android
#endif // ANDROID_INPUT_MOCKS_H_

View file

@ -0,0 +1,88 @@
/*
* Copyright (C) 2015 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 ANDROID_MOCK_INPUT_HOST_H_
#define ANDROID_MOCK_INPUT_HOST_H_
#include "gmock/gmock.h"
#include "InputHost.h"
namespace android {
namespace tests {
class MockInputReport : public InputReport {
public:
MockInputReport() : InputReport(nullptr, {}, nullptr) {}
MOCK_METHOD4(setIntUsage, void(InputCollectionId id, InputUsage usage, int32_t value,
int32_t arityIndex));
MOCK_METHOD4(setBoolUsage, void(InputCollectionId id, InputUsage usage, bool value,
int32_t arityIndex));
MOCK_METHOD1(reportEvent, void(InputDeviceHandle* d));
};
class MockInputReportDefinition : public InputReportDefinition {
public:
MockInputReportDefinition() : InputReportDefinition(nullptr, {}, nullptr) {}
MOCK_METHOD2(addCollection, void(InputCollectionId id, int32_t arity));
MOCK_METHOD5(declareUsage, void(InputCollectionId id, InputUsage usage, int32_t min,
int32_t max, float resolution));
MOCK_METHOD3(declareUsages, void(InputCollectionId id, InputUsage* usage, size_t usageCount));
MOCK_METHOD0(allocateReport, InputReport*());
};
class MockInputDeviceDefinition : public InputDeviceDefinition {
public:
MockInputDeviceDefinition() : InputDeviceDefinition(nullptr, {}, nullptr) {}
MOCK_METHOD1(addReport, void(InputReportDefinition* r));
};
class MockInputProperty : public InputProperty {
public:
MockInputProperty() : InputProperty(nullptr, {}, nullptr) {}
virtual ~MockInputProperty() {}
MOCK_CONST_METHOD0(getKey, const char*());
MOCK_CONST_METHOD0(getValue, const char*());
};
class MockInputPropertyMap : public InputPropertyMap {
public:
MockInputPropertyMap() : InputPropertyMap(nullptr, {}, nullptr) {}
virtual ~MockInputPropertyMap() {}
MOCK_CONST_METHOD1(getDeviceProperty, InputProperty*(const char* key));
MOCK_CONST_METHOD1(freeDeviceProperty, void(InputProperty* property));
};
class MockInputHost : public InputHostInterface {
public:
MOCK_METHOD5(createDeviceIdentifier, InputDeviceIdentifier*(
const char* name, int32_t productId, int32_t vendorId, InputBus bus,
const char* uniqueId));
MOCK_METHOD0(createDeviceDefinition, InputDeviceDefinition*());
MOCK_METHOD0(createInputReportDefinition, InputReportDefinition*());
MOCK_METHOD0(createOutputReportDefinition, InputReportDefinition*());
MOCK_METHOD1(freeReportDefinition, void(InputReportDefinition* reportDef));
MOCK_METHOD2(registerDevice, InputDeviceHandle*(InputDeviceIdentifier* id,
InputDeviceDefinition* d));
MOCK_METHOD1(unregisterDevice, void(InputDeviceHandle* handle));
MOCK_METHOD1(getDevicePropertyMap, InputPropertyMap*(InputDeviceIdentifier* id));
MOCK_METHOD1(freeDevicePropertyMap, void(InputPropertyMap* propertyMap));
};
} // namespace tests
} // namespace android
#endif // ANDROID_MOCK_INPUT_HOST_H_

View file

@ -0,0 +1,106 @@
/*
* Copyright (C) 2015 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 <memory>
#include <linux/input.h>
#include <gtest/gtest.h>
#include "InputMocks.h"
#include "MockInputHost.h"
#include "SwitchInputMapper.h"
using ::testing::_;
using ::testing::Args;
using ::testing::InSequence;
using ::testing::Return;
using ::testing::UnorderedElementsAre;
namespace android {
namespace tests {
class SwitchInputMapperTest : public ::testing::Test {
protected:
virtual void SetUp() override {
mMapper = std::make_unique<SwitchInputMapper>();
}
MockInputHost mHost;
std::unique_ptr<SwitchInputMapper> mMapper;
};
TEST_F(SwitchInputMapperTest, testConfigureDevice) {
MockInputReportDefinition reportDef;
MockInputDeviceNode deviceNode;
deviceNode.addSwitch(SW_LID);
deviceNode.addSwitch(SW_CAMERA_LENS_COVER);
EXPECT_CALL(reportDef, addCollection(INPUT_COLLECTION_ID_SWITCH, 1));
EXPECT_CALL(reportDef, declareUsages(INPUT_COLLECTION_ID_SWITCH, _, 2))
.With(Args<1,2>(UnorderedElementsAre(INPUT_USAGE_SWITCH_LID,
INPUT_USAGE_SWITCH_CAMERA_LENS_COVER)));
mMapper->configureInputReport(&deviceNode, &reportDef);
}
TEST_F(SwitchInputMapperTest, testConfigureDevice_noSwitches) {
MockInputReportDefinition reportDef;
MockInputDeviceNode deviceNode;
EXPECT_CALL(reportDef, addCollection(_, _)).Times(0);
EXPECT_CALL(reportDef, declareUsages(_, _, _)).Times(0);
mMapper->configureInputReport(&deviceNode, &reportDef);
}
TEST_F(SwitchInputMapperTest, testProcessInput) {
MockInputReportDefinition reportDef;
MockInputDeviceNode deviceNode;
deviceNode.addSwitch(SW_LID);
EXPECT_CALL(reportDef, addCollection(_, _));
EXPECT_CALL(reportDef, declareUsages(_, _, _));
mMapper->configureInputReport(&deviceNode, &reportDef);
MockInputReport report;
EXPECT_CALL(reportDef, allocateReport())
.WillOnce(Return(&report));
{
// Test two switch events in order
InSequence s;
EXPECT_CALL(report, setBoolUsage(INPUT_COLLECTION_ID_SWITCH, INPUT_USAGE_SWITCH_LID, 1, 0));
EXPECT_CALL(report, reportEvent(_));
EXPECT_CALL(report, setBoolUsage(INPUT_COLLECTION_ID_SWITCH, INPUT_USAGE_SWITCH_LID, 0, 0));
EXPECT_CALL(report, reportEvent(_));
}
InputEvent events[] = {
{0, EV_SW, SW_LID, 1},
{1, EV_SYN, SYN_REPORT, 0},
{2, EV_SW, SW_LID, 0},
{3, EV_SYN, SYN_REPORT, 0},
};
for (auto e : events) {
mMapper->process(e);
}
}
} // namespace tests
} // namespace android