From 3186e7bf4805b317769470137ffcf1f7eb0215e0 Mon Sep 17 00:00:00 2001 From: Tim Kilbourn Date: Thu, 16 Apr 2015 15:32:08 -0700 Subject: [PATCH] Configure device classes for evdev devices. Change-Id: Ia75b71253771d9d558c59411e27f8a51e352fb8b --- modules/input/evdev/EvdevModule.cpp | 2 +- modules/input/evdev/InputDevice.cpp | 169 +++++++++++- modules/input/evdev/InputDevice.h | 55 +++- modules/input/evdev/InputDeviceManager.cpp | 2 +- modules/input/evdev/InputDeviceManager.h | 5 + modules/input/evdev/InputHost.cpp | 16 +- modules/input/evdev/InputHost.h | 19 +- modules/input/evdev/InputHub-internal.h | 29 +++ modules/input/evdev/InputHub.cpp | 95 ++++++- modules/input/evdev/InputHub.h | 32 ++- tests/input/evdev/Android.mk | 1 + tests/input/evdev/InputDevice_test.cpp | 121 ++++++--- tests/input/evdev/InputHub_test.cpp | 59 +++++ tests/input/evdev/InputMocks.cpp | 406 +++++++++++++++++++++++++++++ tests/input/evdev/InputMocks.h | 256 ++++++++++++++++++ 15 files changed, 1205 insertions(+), 62 deletions(-) create mode 100644 modules/input/evdev/InputHub-internal.h create mode 100644 tests/input/evdev/InputMocks.cpp create mode 100644 tests/input/evdev/InputMocks.h diff --git a/modules/input/evdev/EvdevModule.cpp b/modules/input/evdev/EvdevModule.cpp index f6df219..1171a1a 100644 --- a/modules/input/evdev/EvdevModule.cpp +++ b/modules/input/evdev/EvdevModule.cpp @@ -55,7 +55,7 @@ static std::unique_ptr gEvdevModule; EvdevModule::EvdevModule(InputHost inputHost) : mInputHost(inputHost), - mDeviceManager(std::make_shared()), + mDeviceManager(std::make_shared(mInputHost)), mInputHub(std::make_unique(mDeviceManager)) {} void EvdevModule::init() { diff --git a/modules/input/evdev/InputDevice.cpp b/modules/input/evdev/InputDevice.cpp index 16f8039..883d6d4 100644 --- a/modules/input/evdev/InputDevice.cpp +++ b/modules/input/evdev/InputDevice.cpp @@ -17,10 +17,14 @@ #define LOG_TAG "InputDevice" #define LOG_NDEBUG 0 +// Enables debug output for processing input events +#define DEBUG_INPUT_EVENTS 0 + #include #define __STDC_FORMAT_MACROS #include +#include #include #include @@ -34,18 +38,177 @@ namespace android { -EvdevDevice::EvdevDevice(const std::shared_ptr& node) : - mDeviceNode(node) {} +static InputBus getInputBus(const std::shared_ptr& node) { + switch (node->getBusType()) { + case BUS_USB: + return INPUT_BUS_USB; + case BUS_BLUETOOTH: + return INPUT_BUS_BT; + case BUS_RS232: + return INPUT_BUS_SERIAL; + default: + // TODO: check for other linux bus types that might not be built-in + return INPUT_BUS_BUILTIN; + } +} + +static uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) { + // Touch devices get dibs on touch-related axes. + if (deviceClasses & INPUT_DEVICE_CLASS_TOUCH) { + switch (axis) { + case ABS_X: + case ABS_Y: + case ABS_PRESSURE: + case ABS_TOOL_WIDTH: + case ABS_DISTANCE: + case ABS_TILT_X: + case ABS_TILT_Y: + case ABS_MT_SLOT: + case ABS_MT_TOUCH_MAJOR: + case ABS_MT_TOUCH_MINOR: + case ABS_MT_WIDTH_MAJOR: + case ABS_MT_WIDTH_MINOR: + case ABS_MT_ORIENTATION: + case ABS_MT_POSITION_X: + case ABS_MT_POSITION_Y: + case ABS_MT_TOOL_TYPE: + case ABS_MT_BLOB_ID: + case ABS_MT_TRACKING_ID: + case ABS_MT_PRESSURE: + case ABS_MT_DISTANCE: + return INPUT_DEVICE_CLASS_TOUCH; + } + } + + // External stylus gets the pressure axis + if (deviceClasses & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { + if (axis == ABS_PRESSURE) { + return INPUT_DEVICE_CLASS_EXTERNAL_STYLUS; + } + } + + // Joystick devices get the rest. + return INPUT_DEVICE_CLASS_JOYSTICK; +} + +static bool getBooleanProperty(const InputProperty& prop) { + const char* propValue = prop.getValue(); + if (propValue == nullptr) return false; + + char* end; + int value = std::strtol(propValue, &end, 10); + if (*end != '\0') { + ALOGW("Expected boolean for property %s; value=%s", prop.getKey(), propValue); + return false; + } + 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; + } + + // 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; + } + + // See if this is a touch pad. + // Is this a new modern multi-touch driver? + if (node->hasAbsoluteAxis(ABS_MT_POSITION_X) + && node->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; + } + // 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; + // 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; + } + + // See if this device is a joystick. + // Assumes that joysticks always have gamepad buttons in order to + // distinguish them from other devices such as accelerometers that also have + // absolute axes. + if (haveGamepadButtons) { + uint32_t assumedClasses = *classes | INPUT_DEVICE_CLASS_JOYSTICK; + for (int i = 0; i < ABS_CNT; ++i) { + if (node->hasAbsoluteAxis(i) + && getAbsAxisUsage(i, assumedClasses) == INPUT_DEVICE_CLASS_JOYSTICK) { + *classes = assumedClasses; + break; + } + } + } + + // Check whether this device has switches. + for (int i = 0; i < SW_CNT; ++i) { + if (node->hasSwitch(i)) { + *classes |= INPUT_DEVICE_CLASS_SWITCH; + break; + } + } + + // Check whether this device supports the vibrator. + if (node->hasForceFeedback(FF_RUMBLE)) { + *classes |= INPUT_DEVICE_CLASS_VIBRATOR; + } + + // 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); +} + +EvdevDevice::EvdevDevice(InputHost host, const std::shared_ptr& node) : + mHost(host), mDeviceNode(node) { + + 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); +} void EvdevDevice::processInput(InputEvent& event, nsecs_t currentTime) { +#if DEBUG_INPUT_EVENTS std::string log; log.append("---InputEvent for device %s---\n"); log.append(" when: %" PRId64 "\n"); log.append(" type: %d\n"); log.append(" code: %d\n"); log.append(" value: %d\n"); - ALOGV(log.c_str(), mDeviceNode->getPath().c_str(), event.when, event.type, event.code, + ALOGD(log.c_str(), mDeviceNode->getPath().c_str(), event.when, event.type, event.code, event.value); +#endif if (event.type == EV_MSC) { if (event.code == MSC_ANDROID_TIME_SEC) { diff --git a/modules/input/evdev/InputDevice.h b/modules/input/evdev/InputDevice.h index 7a99f90..b4f3244 100644 --- a/modules/input/evdev/InputDevice.h +++ b/modules/input/evdev/InputDevice.h @@ -21,6 +21,7 @@ #include +#include "InputHost.h" #include "InputHub.h" namespace android { @@ -33,6 +34,7 @@ class InputDeviceInterface { public: virtual void processInput(InputEvent& event, nsecs_t currentTime) = 0; + virtual uint32_t getInputClasses() = 0; protected: InputDeviceInterface() = default; virtual ~InputDeviceInterface() = default; @@ -43,18 +45,69 @@ protected: */ class EvdevDevice : public InputDeviceInterface { public: - explicit EvdevDevice(const std::shared_ptr& node); + EvdevDevice(InputHost host, const std::shared_ptr& node); virtual ~EvdevDevice() override = default; virtual void processInput(InputEvent& event, nsecs_t currentTime) override; + virtual uint32_t getInputClasses() override { return mClasses; } private: + InputHost mHost; std::shared_ptr mDeviceNode; + InputDeviceIdentifier mInputId; + uint32_t mClasses = 0; int32_t mOverrideSec = 0; int32_t mOverrideUsec = 0; }; +/* Input device classes. */ +enum { + /* The input device is a keyboard or has buttons. */ + INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001, + + /* The input device is an alpha-numeric keyboard (not just a dial pad). */ + INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002, + + /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */ + INPUT_DEVICE_CLASS_TOUCH = 0x00000004, + + /* The input device is a cursor device such as a trackball or mouse. */ + INPUT_DEVICE_CLASS_CURSOR = 0x00000008, + + /* The input device is a multi-touch touchscreen. */ + INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010, + + /* The input device is a directional pad (implies keyboard, has DPAD keys). */ + INPUT_DEVICE_CLASS_DPAD = 0x00000020, + + /* The input device is a gamepad (implies keyboard, has BUTTON keys). */ + INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040, + + /* The input device has switches. */ + INPUT_DEVICE_CLASS_SWITCH = 0x00000080, + + /* The input device is a joystick (implies gamepad, has joystick absolute axes). */ + INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100, + + /* The input device has a vibrator (supports FF_RUMBLE). */ + INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200, + + /* The input device has a microphone. */ + // TODO: remove this and let the host take care of it + INPUT_DEVICE_CLASS_MIC = 0x00000400, + + /* The input device is an external stylus (has data we want to fuse with touch data). */ + INPUT_DEVICE_CLASS_EXTERNAL_STYLUS = 0x00000800, + + /* The input device is virtual (not a real device, not part of UI configuration). */ + /* not used - INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000, */ + + /* The input device is external (not built-in). */ + // TODO: remove this and let the host take care of it? + INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000, +}; + } // namespace android #endif // ANDROID_INPUT_DEVICE_H_ diff --git a/modules/input/evdev/InputDeviceManager.cpp b/modules/input/evdev/InputDeviceManager.cpp index 79a9610..f21d6d1 100644 --- a/modules/input/evdev/InputDeviceManager.cpp +++ b/modules/input/evdev/InputDeviceManager.cpp @@ -34,7 +34,7 @@ void InputDeviceManager::onInputEvent(const std::shared_ptr& no } void InputDeviceManager::onDeviceAdded(const std::shared_ptr& node) { - mDevices[node] = std::make_shared(node); + mDevices[node] = std::make_shared(mHost, node); } void InputDeviceManager::onDeviceRemoved(const std::shared_ptr& node) { diff --git a/modules/input/evdev/InputDeviceManager.h b/modules/input/evdev/InputDeviceManager.h index 2c0ffc8..25dd912 100644 --- a/modules/input/evdev/InputDeviceManager.h +++ b/modules/input/evdev/InputDeviceManager.h @@ -23,6 +23,7 @@ #include #include "InputDevice.h" +#include "InputHost.h" #include "InputHub.h" namespace android { @@ -34,6 +35,8 @@ namespace android { */ class InputDeviceManager : public InputCallbackInterface { public: + explicit InputDeviceManager(InputHost host) : + mHost(host) {} virtual ~InputDeviceManager() override = default; virtual void onInputEvent(const std::shared_ptr& node, InputEvent& event, @@ -42,6 +45,8 @@ public: virtual void onDeviceRemoved(const std::shared_ptr& node) override; private: + InputHost mHost; + template using DeviceMap = std::unordered_map, std::shared_ptr>; diff --git a/modules/input/evdev/InputHost.cpp b/modules/input/evdev/InputHost.cpp index 6a65fcd..74a5f8a 100644 --- a/modules/input/evdev/InputHost.cpp +++ b/modules/input/evdev/InputHost.cpp @@ -51,11 +51,16 @@ InputProperty::~InputProperty() { mCallbacks.input_free_device_property(mHost, mProperty); } -const char* InputProperty::getKey() { +InputProperty::InputProperty(InputProperty&& rhs) : + InputHostBase(rhs), mProperty(std::move(rhs.mProperty)) { + rhs.mProperty = nullptr; +} + +const char* InputProperty::getKey() const { return mCallbacks.input_get_property_key(mHost, mProperty); } -const char* InputProperty::getValue() { +const char* InputProperty::getValue() const { return mCallbacks.input_get_property_value(mHost, mProperty); } @@ -63,7 +68,12 @@ InputPropertyMap::~InputPropertyMap() { mCallbacks.input_free_device_property_map(mHost, mMap); } -InputProperty InputPropertyMap::getDeviceProperty(const char* key) { +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, mCallbacks.input_get_device_property(mHost, mMap, key)); } diff --git a/modules/input/evdev/InputHost.h b/modules/input/evdev/InputHost.h index 98ce26f..d6a04d9 100644 --- a/modules/input/evdev/InputHost.h +++ b/modules/input/evdev/InputHost.h @@ -43,6 +43,9 @@ 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; + input_host_t* mHost; input_host_callbacks_t mCallbacks; }; @@ -117,12 +120,11 @@ public: operator input_property_t*() { return mProperty; } - const char* getKey(); - const char* getValue(); + const char* getKey() const; + const char* getValue() const; - // Default move constructor transfers ownership of the input_property_t - // pointer. - InputProperty(InputProperty&& rhs) = default; + // 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. @@ -145,11 +147,10 @@ public: operator input_property_map_t*() { return mMap; } - InputProperty getDeviceProperty(const char* key); + InputProperty getDeviceProperty(const char* key) const; - // Default move constructor transfers ownership of the input_property_map_t - // pointer. - InputPropertyMap(InputPropertyMap&& rhs) = default; + // 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. diff --git a/modules/input/evdev/InputHub-internal.h b/modules/input/evdev/InputHub-internal.h new file mode 100644 index 0000000..b4f1297 --- /dev/null +++ b/modules/input/evdev/InputHub-internal.h @@ -0,0 +1,29 @@ +/* + * 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_HUB_INTERNAL_H_ +#define ANDROID_INPUT_HUB_INTERNAL_H_ + +namespace android { +namespace internal { + +/** Test whether any bits in the interval [start, end) are set in the array. */ +bool testBitInRange(const uint8_t arr[], size_t start, size_t end); + +} // namespace internal +} // namespace android + +#endif // ANDROID_INPUT_HUB_INTERNAL_H_ diff --git a/modules/input/evdev/InputHub.cpp b/modules/input/evdev/InputHub.cpp index ee64b29..8c48345 100644 --- a/modules/input/evdev/InputHub.cpp +++ b/modules/input/evdev/InputHub.cpp @@ -15,7 +15,10 @@ */ #define LOG_TAG "InputHub" -#define LOG_NDEBUG 0 +//#define LOG_NDEBUG 0 + +// Enables debug output for hasKeyInRange +#define DEBUG_KEY_RANGE 0 #include #include @@ -34,6 +37,7 @@ #include #include "InputHub.h" +#include "InputHub-internal.h" #include #include @@ -56,6 +60,57 @@ static constexpr size_t sizeofBitArray(size_t bits) { return (bits + 7) / 8; } +namespace internal { + +#if DEBUG_KEY_RANGE +static const char* bitstrings[16] = { + "0000", "0001", "0010", "0011", + "0100", "0101", "0110", "0111", + "1000", "1001", "1010", "1011", + "1100", "1101", "1110", "1111", +}; +#endif + +bool testBitInRange(const uint8_t arr[], size_t start, size_t end) { +#if DEBUG_KEY_RANGE + ALOGD("testBitInRange(%d, %d)", start, end); +#endif + // Invalid range! This is nonsense; just say no. + if (end <= start) return false; + + // Find byte array indices. The end is not included in the range, nor is + // endIndex. Round up for endIndex. + size_t startIndex = start / 8; + size_t endIndex = (end + 7) / 8; +#if DEBUG_KEY_RANGE + ALOGD("startIndex=%d, endIndex=%d", startIndex, endIndex); +#endif + for (size_t i = startIndex; i < endIndex; ++i) { + uint8_t bits = arr[i]; + uint8_t mask = 0xff; +#if DEBUG_KEY_RANGE + ALOGD("block %04d: %s%s", i, bitstrings[bits >> 4], bitstrings[bits & 0x0f]); +#endif + if (bits) { + // Mask off bits before our start bit + if (i == startIndex) { + mask &= 0xff << (start % 8); + } + // Mask off bits after our end bit + if (i == endIndex - 1 && (end % 8)) { + mask &= 0xff >> (8 - (end % 8)); + } +#if DEBUG_KEY_RANGE + ALOGD("mask: %s%s", bitstrings[mask >> 4], bitstrings[mask & 0x0f]); +#endif + // Test the index against the mask + if (bits & mask) return true; + } + } + return false; +} +} // namespace internal + static void getLinuxRelease(int* major, int* minor) { struct utsname info; if (uname(&info) || sscanf(info.release, "%d.%d", major, minor) <= 0) { @@ -74,7 +129,6 @@ static bool processHasCapability(int capability) { caphdr->version = _LINUX_CAPABILITY_VERSION_3; LOG_ALWAYS_FATAL_IF(capget(caphdr, capdata) != 0, "Could not get process capabilities. errno=%d", errno); - ALOGV("effective capabilities: %08x %08x", capdata[0].effective, capdata[1].effective); int idx = CAP_TO_INDEX(capability); return capdata[idx].effective & CAP_TO_MASK(capability); } @@ -102,16 +156,20 @@ public: virtual uint16_t getVersion() const override { return mVersion; } virtual bool hasKey(int32_t key) const override; - virtual bool hasRelativeAxis(int axis) const override; - virtual const AbsoluteAxisInfo* getAbsoluteAxisInfo(int32_t axis) const override; + virtual bool hasKeyInRange(int32_t start, int32_t end) const override; + virtual bool hasRelativeAxis(int32_t axis) const override; + virtual bool hasAbsoluteAxis(int32_t axis) const override; + virtual bool hasSwitch(int32_t sw) const override; + virtual bool hasForceFeedback(int32_t ff) const override; virtual bool hasInputProperty(int property) const override; virtual int32_t getKeyState(int32_t key) const override; virtual int32_t getSwitchState(int32_t sw) const override; + virtual const AbsoluteAxisInfo* getAbsoluteAxisInfo(int32_t axis) const override; virtual status_t getAbsoluteAxisValue(int32_t axis, int32_t* outValue) const override; virtual void vibrate(nsecs_t duration) override; - virtual void cancelVibrate(int32_t deviceId) override; + virtual void cancelVibrate() override; virtual void disableDriverKeyRepeat() override; @@ -272,6 +330,10 @@ bool EvdevDeviceNode::hasKey(int32_t key) const { return false; } +bool EvdevDeviceNode::hasKeyInRange(int32_t startKey, int32_t endKey) const { + return internal::testBitInRange(mKeyBitmask, startKey, endKey); +} + bool EvdevDeviceNode::hasRelativeAxis(int axis) const { if (axis >= 0 && axis <= REL_MAX) { return testBit(axis, mRelBitmask); @@ -279,6 +341,13 @@ bool EvdevDeviceNode::hasRelativeAxis(int axis) const { return false; } +bool EvdevDeviceNode::hasAbsoluteAxis(int axis) const { + if (axis >= 0 && axis <= ABS_MAX) { + return getAbsoluteAxisInfo(axis) != nullptr; + } + return false; +} + const AbsoluteAxisInfo* EvdevDeviceNode::getAbsoluteAxisInfo(int32_t axis) const { if (axis < 0 || axis > ABS_MAX) { return nullptr; @@ -291,6 +360,20 @@ const AbsoluteAxisInfo* EvdevDeviceNode::getAbsoluteAxisInfo(int32_t axis) const return nullptr; } +bool EvdevDeviceNode::hasSwitch(int32_t sw) const { + if (sw >= 0 && sw <= SW_MAX) { + return testBit(sw, mSwBitmask); + } + return false; +} + +bool EvdevDeviceNode::hasForceFeedback(int32_t ff) const { + if (ff >= 0 && ff <= FF_MAX) { + return testBit(ff, mFfBitmask); + } + return false; +} + bool EvdevDeviceNode::hasInputProperty(int property) const { if (property >= 0 && property <= INPUT_PROP_MAX) { return testBit(property, mPropBitmask); @@ -371,7 +454,7 @@ void EvdevDeviceNode::vibrate(nsecs_t duration) { mFfEffectPlaying = true; } -void EvdevDeviceNode::cancelVibrate(int32_t deviceId) { +void EvdevDeviceNode::cancelVibrate() { if (mFfEffectPlaying) { mFfEffectPlaying = false; diff --git a/modules/input/evdev/InputHub.h b/modules/input/evdev/InputHub.h index dfab3db..1abdc09 100644 --- a/modules/input/evdev/InputHub.h +++ b/modules/input/evdev/InputHub.h @@ -56,29 +56,55 @@ struct AbsoluteAxisInfo { */ class InputDeviceNode { public: + /** Get the Linux device path for the node. */ virtual const std::string& getPath() const = 0; + /** Get the name of the device returned by the driver. */ virtual const std::string& getName() const = 0; + /** Get the location of the device returned by the driver. */ virtual const std::string& getLocation() const = 0; + /** Get the unique id of the device returned by the driver. */ virtual const std::string& getUniqueId() const = 0; + /** Get the bus type of the device returned by the driver. */ virtual uint16_t getBusType() const = 0; + /** Get the vendor id of the device returned by the driver. */ virtual uint16_t getVendorId() const = 0; + /** Get the product id of the device returned by the driver. */ virtual uint16_t getProductId() const = 0; + /** Get the version of the device driver. */ virtual uint16_t getVersion() const = 0; + /** Returns true if the device has the key. */ virtual bool hasKey(int32_t key) const = 0; - virtual bool hasRelativeAxis(int axis) const = 0; - virtual const AbsoluteAxisInfo* getAbsoluteAxisInfo(int32_t axis) const = 0; + /** Returns true if the device has a key in the range [startKey, endKey). */ + virtual bool hasKeyInRange(int32_t startKey, int32_t endKey) const = 0; + /** Returns true if the device has the relative axis. */ + virtual bool hasRelativeAxis(int32_t axis) const = 0; + /** Returns true if the device has the absolute axis. */ + virtual bool hasAbsoluteAxis(int32_t axis) const = 0; + /** Returns true if the device has the switch. */ + virtual bool hasSwitch(int32_t sw) const = 0; + /** Returns true if the device has the force feedback method. */ + virtual bool hasForceFeedback(int32_t ff) const = 0; + /** Returns true if the device has the input property. */ virtual bool hasInputProperty(int property) const = 0; + /** Returns the state of the key. */ virtual int32_t getKeyState(int32_t key) const = 0; + /** Returns the state of the switch. */ virtual int32_t getSwitchState(int32_t sw) const = 0; + /** Returns information about the absolute axis. */ + virtual const AbsoluteAxisInfo* getAbsoluteAxisInfo(int32_t axis) const = 0; + /** Returns the value of the absolute axis. */ virtual status_t getAbsoluteAxisValue(int32_t axis, int32_t* outValue) const = 0; + /** Vibrate the device for duration ns. */ virtual void vibrate(nsecs_t duration) = 0; - virtual void cancelVibrate(int32_t deviceId) = 0; + /** Stop vibration on the device. */ + virtual void cancelVibrate() = 0; + /** Disable key repeat for the device in the driver. */ virtual void disableDriverKeyRepeat() = 0; protected: diff --git a/tests/input/evdev/Android.mk b/tests/input/evdev/Android.mk index 167cbc2..544e5a8 100644 --- a/tests/input/evdev/Android.mk +++ b/tests/input/evdev/Android.mk @@ -6,6 +6,7 @@ LOCAL_C_INCLUDES += hardware/libhardware/modules/input/evdev LOCAL_SRC_FILES:= \ InputDevice_test.cpp \ InputHub_test.cpp \ + InputMocks.cpp \ TestHelpers.cpp LOCAL_SHARED_LIBRARIES := \ diff --git a/tests/input/evdev/InputDevice_test.cpp b/tests/input/evdev/InputDevice_test.cpp index a96d664..123f2b8 100644 --- a/tests/input/evdev/InputDevice_test.cpp +++ b/tests/input/evdev/InputDevice_test.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "InputHub_test" //#define LOG_NDEBUG 0 +#include + #include #include @@ -24,7 +26,9 @@ #include #include "InputDevice.h" +#include "InputHost.h" #include "InputHub.h" +#include "InputMocks.h" // # of milliseconds to allow for timing measurements #define TIMING_TOLERANCE_MS 25 @@ -35,42 +39,23 @@ namespace android { namespace tests { -class MockInputDeviceNode : public InputDeviceNode { - virtual const std::string& getPath() const override { return mPath; } - - virtual const std::string& getName() const override { return mName; } - virtual const std::string& getLocation() const override { return mLocation; } - virtual const std::string& getUniqueId() const override { return mUniqueId; } - - virtual uint16_t getBusType() const override { return 0; } - virtual uint16_t getVendorId() const override { return 0; } - virtual uint16_t getProductId() const override { return 0; } - virtual uint16_t getVersion() const override { return 0; } - - virtual bool hasKey(int32_t key) const { return false; } - virtual bool hasRelativeAxis(int axis) const { return false; } - virtual bool hasInputProperty(int property) const { return false; } - - virtual int32_t getKeyState(int32_t key) const { return 0; } - virtual int32_t getSwitchState(int32_t sw) const { return 0; } - virtual const AbsoluteAxisInfo* getAbsoluteAxisInfo(int32_t axis) const { return nullptr; } - virtual status_t getAbsoluteAxisValue(int32_t axis, int32_t* outValue) const { return 0; } - - virtual void vibrate(nsecs_t duration) {} - virtual void cancelVibrate(int32_t deviceId) {} +class EvdevDeviceTest : public ::testing::Test { +protected: + virtual void SetUp() override { + mMockHost.reset(new MockInputHost()); + } - virtual void disableDriverKeyRepeat() {} + virtual void TearDown() override { + ASSERT_TRUE(mMockHost->checkAllocations()); + } -private: - std::string mPath = "/test"; - std::string mName = "Test Device"; - std::string mLocation = "test/0"; - std::string mUniqueId = "test-id"; + std::unique_ptr mMockHost; }; -TEST(EvdevDeviceTest, testOverrideTime) { +TEST_F(EvdevDeviceTest, testOverrideTime) { + InputHost host = {mMockHost.get(), kTestCallbacks}; auto node = std::make_shared(); - auto device = std::make_unique(node); + auto device = std::make_unique(host, node); ASSERT_TRUE(device != nullptr); // Send two timestamp override events before an input event. @@ -97,9 +82,10 @@ TEST(EvdevDeviceTest, testOverrideTime) { EXPECT_EQ(when, keyUp.when); } -TEST(EvdevDeviceTest, testWrongClockCorrection) { +TEST_F(EvdevDeviceTest, testWrongClockCorrection) { + InputHost host = {mMockHost.get(), kTestCallbacks}; auto node = std::make_shared(); - auto device = std::make_unique(node); + auto device = std::make_unique(host, node); ASSERT_TRUE(device != nullptr); auto now = systemTime(SYSTEM_TIME_MONOTONIC); @@ -113,9 +99,10 @@ TEST(EvdevDeviceTest, testWrongClockCorrection) { EXPECT_NEAR(now, event.when, ms2ns(TIMING_TOLERANCE_MS)); } -TEST(EvdevDeviceTest, testClockCorrectionOk) { +TEST_F(EvdevDeviceTest, testClockCorrectionOk) { + InputHost host = {mMockHost.get(), kTestCallbacks}; auto node = std::make_shared(); - auto device = std::make_unique(node); + auto device = std::make_unique(host, node); ASSERT_TRUE(device != nullptr); auto now = systemTime(SYSTEM_TIME_MONOTONIC); @@ -130,5 +117,69 @@ TEST(EvdevDeviceTest, testClockCorrectionOk) { EXPECT_NEAR(now, event.when, ms2ns(TIMING_TOLERANCE_MS)); } +TEST_F(EvdevDeviceTest, testN7v2Touchscreen) { + InputHost host = {mMockHost.get(), kTestCallbacks}; + auto node = std::shared_ptr(MockNexus7v2::getElanTouchscreen()); + auto device = std::make_unique(host, 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(MockNexus7v2::getButtonJack()); + auto device = std::make_unique(host, node); + EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses()); +} + +TEST_F(EvdevDeviceTest, testN7v2HeadsetJack) { + InputHost host = {mMockHost.get(), kTestCallbacks}; + auto node = std::shared_ptr(MockNexus7v2::getHeadsetJack()); + auto device = std::make_unique(host, node); + EXPECT_EQ(INPUT_DEVICE_CLASS_SWITCH, device->getInputClasses()); +} + +TEST_F(EvdevDeviceTest, testN7v2H2wButton) { + InputHost host = {mMockHost.get(), kTestCallbacks}; + auto node = std::shared_ptr(MockNexus7v2::getH2wButton()); + auto device = std::make_unique(host, node); + EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses()); +} + +TEST_F(EvdevDeviceTest, testN7v2GpioKeys) { + InputHost host = {mMockHost.get(), kTestCallbacks}; + auto node = std::shared_ptr(MockNexus7v2::getGpioKeys()); + auto device = std::make_unique(host, node); + EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses()); +} + +TEST_F(EvdevDeviceTest, testNexusPlayerGpioKeys) { + InputHost host = {mMockHost.get(), kTestCallbacks}; + auto node = std::shared_ptr(MockNexusPlayer::getGpioKeys()); + auto device = std::make_unique(host, node); + EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses()); +} + +TEST_F(EvdevDeviceTest, testNexusPlayerMidPowerBtn) { + InputHost host = {mMockHost.get(), kTestCallbacks}; + auto node = std::shared_ptr(MockNexusPlayer::getMidPowerBtn()); + auto device = std::make_unique(host, node); + EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses()); +} + +TEST_F(EvdevDeviceTest, testNexusRemote) { + InputHost host = {mMockHost.get(), kTestCallbacks}; + auto node = std::shared_ptr(MockNexusPlayer::getNexusRemote()); + auto device = std::make_unique(host, node); + EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses()); +} + +TEST_F(EvdevDeviceTest, testAsusGamepad) { + InputHost host = {mMockHost.get(), kTestCallbacks}; + auto node = std::shared_ptr(MockNexusPlayer::getAsusGamepad()); + auto device = std::make_unique(host, node); + EXPECT_EQ(INPUT_DEVICE_CLASS_JOYSTICK|INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses()); +} + } // namespace tests } // namespace android diff --git a/tests/input/evdev/InputHub_test.cpp b/tests/input/evdev/InputHub_test.cpp index f2967b9..5864a95 100644 --- a/tests/input/evdev/InputHub_test.cpp +++ b/tests/input/evdev/InputHub_test.cpp @@ -30,6 +30,7 @@ #include #include "InputHub.h" +#include "InputHub-internal.h" #include "TestHelpers.h" // # of milliseconds to fudge stopwatch measurements @@ -256,5 +257,63 @@ TEST_F(InputHubTest, DISABLED_testCallbackOrder) { EXPECT_TRUE(deviceCallbackFinished); } +using internal::testBitInRange; + +TEST(BitInRange, testInvalidRange) { + uint8_t arr[2] = { 0xff, 0xff }; + EXPECT_FALSE(testBitInRange(arr, 0, 0)); + EXPECT_FALSE(testBitInRange(arr, 1, 0)); +} + +TEST(BitInRange, testNoBits) { + uint8_t arr[1]; + arr[0] = 0; + EXPECT_FALSE(testBitInRange(arr, 0, 8)); +} + +TEST(BitInRange, testOneBit) { + uint8_t arr[1]; + for (int i = 0; i < 8; ++i) { + arr[0] = 1 << i; + EXPECT_TRUE(testBitInRange(arr, 0, 8)); + } +} + +TEST(BitInRange, testZeroStart) { + uint8_t arr[1] = { 0x10 }; + for (int i = 0; i < 5; ++i) { + EXPECT_FALSE(testBitInRange(arr, 0, i)); + } + for (int i = 5; i <= 8; ++i) { + EXPECT_TRUE(testBitInRange(arr, 0, i)); + } +} + +TEST(BitInRange, testByteBoundaryEnd) { + uint8_t arr[1] = { 0x10 }; + for (int i = 0; i < 5; ++i) { + EXPECT_TRUE(testBitInRange(arr, i, 8)); + } + for (int i = 5; i <= 8; ++i) { + EXPECT_FALSE(testBitInRange(arr, i, 8)); + } +} + +TEST(BitInRange, testMultiByteArray) { + // bits set: 11 and 16 + uint8_t arr[3] = { 0x00, 0x08, 0x01 }; + for (int start = 0; start < 24; ++start) { + for (int end = start + 1; end <= 24; ++end) { + if (start > 16 || end <= 11 || (start > 11 && end <= 16)) { + EXPECT_FALSE(testBitInRange(arr, start, end)) + << "range = (" << start << ", " << end << ")"; + } else { + EXPECT_TRUE(testBitInRange(arr, start, end)) + << "range = (" << start << ", " << end << ")"; + } + } + } +} + } // namespace tests } // namespace android diff --git a/tests/input/evdev/InputMocks.cpp b/tests/input/evdev/InputMocks.cpp new file mode 100644 index 0000000..c316075 --- /dev/null +++ b/tests/input/evdev/InputMocks.cpp @@ -0,0 +1,406 @@ +#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; + return *iter < endKey; +} + +namespace MockNexus7v2 { + +MockInputDeviceNode* getElanTouchscreen() { + auto node = new MockInputDeviceNode(); + node->setPath("/dev/input/event0"); + node->setName("elan-touchscreen"); + // Location not set + // UniqueId not set + node->setBusType(0); + node->setVendorId(0); + node->setProductId(0); + node->setVersion(0); + // No keys + // No relative axes + // TODO: set the AbsoluteAxisInfo pointers + node->addAbsAxis(ABS_MT_SLOT, nullptr); + node->addAbsAxis(ABS_MT_TOUCH_MAJOR, nullptr); + node->addAbsAxis(ABS_MT_POSITION_X, nullptr); + node->addAbsAxis(ABS_MT_POSITION_Y, nullptr); + node->addAbsAxis(ABS_MT_TRACKING_ID, nullptr); + node->addAbsAxis(ABS_MT_PRESSURE, nullptr); + // No switches + // No forcefeedback + node->addInputProperty(INPUT_PROP_DIRECT); + return node; +} + +MockInputDeviceNode* getLidInput() { + auto node = new MockInputDeviceNode(); + node->setPath("/dev/input/event1"); + node->setName("lid_input"); + node->setLocation("/dev/input/lid_indev"); + // UniqueId not set + node->setBusType(0); + node->setVendorId(0); + node->setProductId(0); + node->setVersion(0); + // No keys + // No relative axes + // No absolute axes + node->addSwitch(SW_LID); + // No forcefeedback + node->addInputProperty(INPUT_PROP_DIRECT); + return node; +} + +MockInputDeviceNode* getButtonJack() { + auto node = new MockInputDeviceNode(); + node->setPath("/dev/input/event2"); + node->setName("apq8064-tabla-snd-card Button Jack"); + node->setLocation("ALSA"); + // UniqueId not set + node->setBusType(0); + node->setVendorId(0); + node->setProductId(0); + node->setVersion(0); + node->addKeys(BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7); + // No relative axes + // No absolute axes + // No switches + // No forcefeedback + node->addInputProperty(INPUT_PROP_DIRECT); + return node; +} + +MockInputDeviceNode* getHeadsetJack() { + auto node = new MockInputDeviceNode(); + node->setPath("/dev/input/event3"); + node->setName("apq8064-tabla-snd-card Headset Jack"); + node->setLocation("ALSA"); + // UniqueId not set + node->setBusType(0); + node->setVendorId(0); + node->setProductId(0); + node->setVersion(0); + // No keys + // No relative axes + // No absolute axes + node->addSwitch(SW_HEADPHONE_INSERT); + node->addSwitch(SW_MICROPHONE_INSERT); + node->addSwitch(SW_LINEOUT_INSERT); + // ASUS adds some proprietary switches, but we'll only see two of them. + node->addSwitch(0x0e); // SW_HPHL_OVERCURRENT + node->addSwitch(0x0f); // SW_HPHR_OVERCURRENT + // No forcefeedback + node->addInputProperty(INPUT_PROP_DIRECT); + return node; +} + +MockInputDeviceNode* getH2wButton() { + auto node = new MockInputDeviceNode(); + node->setPath("/dev/input/event4"); + node->setName("h2w button"); + // Location not set + // UniqueId not set + node->setBusType(0); + node->setVendorId(0); + node->setProductId(0); + node->setVersion(0); + node->addKeys(KEY_MEDIA); + // No relative axes + // No absolute axes + // No switches + node->addInputProperty(INPUT_PROP_DIRECT); + return node; +} + +MockInputDeviceNode* getGpioKeys() { + auto node = new MockInputDeviceNode(); + node->setPath("/dev/input/event5"); + node->setName("gpio-keys"); + node->setLocation("gpio-keys/input0"); + // UniqueId not set + node->setBusType(0x0019); + node->setVendorId(0x0001); + node->setProductId(0x0001); + node->setVersion(0x0100); + node->addKeys(KEY_VOLUMEDOWN, KEY_VOLUMEUP, KEY_POWER); + // No relative axes + // No absolute axes + // No switches + node->addInputProperty(INPUT_PROP_DIRECT); + return node; +} + +} // namespace MockNexus7v2 + +namespace MockNexusPlayer { + +MockInputDeviceNode* getGpioKeys() { + auto node = new MockInputDeviceNode(); + node->setPath("/dev/input/event0"); + node->setName("gpio-keys"); + node->setLocation("gpio-keys/input0"); + // UniqueId not set + node->setBusType(0x0019); + node->setVendorId(0x0001); + node->setProductId(0x0001); + node->setVersion(0x0100); + node->addKeys(KEY_CONNECT); + // No relative axes + // No absolute axes + // No switches + node->addInputProperty(INPUT_PROP_DIRECT); + return node; +} + +MockInputDeviceNode* getMidPowerBtn() { + auto node = new MockInputDeviceNode(); + node->setPath("/dev/input/event1"); + node->setName("mid_powerbtn"); + node->setLocation("power-button/input0"); + // UniqueId not set + node->setBusType(0x0019); + node->setVendorId(0); + node->setProductId(0); + node->setVersion(0); + node->addKeys(KEY_POWER); + // No relative axes + // No absolute axes + // No switches + node->addInputProperty(INPUT_PROP_DIRECT); + return node; +} + +MockInputDeviceNode* getNexusRemote() { + auto node = new MockInputDeviceNode(); + node->setPath("/dev/input/event2"); + node->setName("Nexus Remote"); + // Location not set + node->setUniqueId("78:86:D9:50:A0:54"); + node->setBusType(0x0005); + node->setVendorId(0x18d1); + node->setProductId(0x2c42); + node->setVersion(0); + node->addKeys(KEY_UP, KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_BACK, KEY_PLAYPAUSE, + KEY_HOMEPAGE, KEY_SEARCH, KEY_SELECT); + // No relative axes + node->addAbsAxis(ABS_MISC, nullptr); + // No switches + node->addInputProperty(INPUT_PROP_DIRECT); + return node; +} + +MockInputDeviceNode* getAsusGamepad() { + auto node = new MockInputDeviceNode(); + node->setPath("/dev/input/event3"); + node->setName("ASUS Gamepad"); + // Location not set + node->setUniqueId("C5:30:CD:50:A0:54"); + node->setBusType(0x0005); + node->setVendorId(0x0b05); + node->setProductId(0x4500); + node->setVersion(0x0040); + node->addKeys(KEY_BACK, KEY_HOMEPAGE, BTN_A, BTN_B, BTN_X, BTN_Y, BTN_TL, BTN_TR, + BTN_MODE, BTN_THUMBL, BTN_THUMBR); + // No relative axes + node->addAbsAxis(ABS_X, nullptr); + node->addAbsAxis(ABS_Y, nullptr); + node->addAbsAxis(ABS_Z, nullptr); + node->addAbsAxis(ABS_RZ, nullptr); + node->addAbsAxis(ABS_GAS, nullptr); + node->addAbsAxis(ABS_BRAKE, nullptr); + node->addAbsAxis(ABS_HAT0X, nullptr); + node->addAbsAxis(ABS_HAT0Y, nullptr); + node->addAbsAxis(ABS_MISC, nullptr); + node->addAbsAxis(0x29, nullptr); + node->addAbsAxis(0x2a, nullptr); + // No switches + node->addInputProperty(INPUT_PROP_DIRECT); + // Note: this device has MSC and LED bitmaps as well. + return node; +} + +} // 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(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(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(host); + return mockHost->getDeviceProperty(map, key); +} + +const char* input_get_property_key(input_host_t* host, input_property_t* property) { + auto mockHost = static_cast(host); + return mockHost->getPropertyKey(property); +} + +const char* input_get_property_value(input_host_t* host, input_property_t* property) { + auto mockHost = static_cast(host); + return mockHost->getPropertyValue(property); +} + +void input_free_device_property(input_host_t* host, input_property_t* property) { + auto mockHost = static_cast(host); + return mockHost->freeDeviceProperty(property); +} + +void input_free_device_property_map(input_host_t* host, input_property_map_t* map) { + auto mockHost = static_cast(host); + return mockHost->freeDevicePropertyMap(map); +} + +} // namespace android diff --git a/tests/input/evdev/InputMocks.h b/tests/input/evdev/InputMocks.h new file mode 100644 index 0000000..5c6eb80 --- /dev/null +++ b/tests/input/evdev/InputMocks.h @@ -0,0 +1,256 @@ +/* + * 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_MOCKS_H_ +#define ANDROID_INPUT_MOCKS_H_ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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 mDeviceId; + int32_t mMapAllocations = 0; + std::unordered_map mPropertyAllocations; +}; + +class MockInputDeviceNode : public InputDeviceNode { +public: + MockInputDeviceNode() = default; + virtual ~MockInputDeviceNode() = default; + + virtual const std::string& getPath() const override { return mPath; } + virtual const std::string& getName() const override { return mName; } + virtual const std::string& getLocation() const override { return mLocation; } + virtual const std::string& getUniqueId() const override { return mUniqueId; } + + void setPath(const std::string& path) { mPath = path; } + void setName(const std::string& name) { mName = name; } + void setLocation(const std::string& location) { mLocation = location; } + void setUniqueId(const std::string& uniqueId) { mUniqueId = uniqueId; } + + virtual uint16_t getBusType() const override { return mBusType; } + virtual uint16_t getVendorId() const override { return mVendorId; } + virtual uint16_t getProductId() const override { return mProductId; } + virtual uint16_t getVersion() const override { return mVersion; } + + void setBusType(uint16_t busType) { mBusType = busType; } + void setVendorId(uint16_t vendorId) { mVendorId = vendorId; } + void setProductId(uint16_t productId) { mProductId = productId; } + void setVersion(uint16_t version) { mVersion = version; } + + virtual bool hasKey(int32_t key) const override { return mKeys.count(key); } + virtual bool hasKeyInRange(int32_t startKey, int32_t endKey) const override; + virtual bool hasRelativeAxis(int axis) const override { return mRelAxes.count(axis); } + virtual bool hasAbsoluteAxis(int32_t axis) const override { return mAbsAxes.count(axis); } + virtual bool hasSwitch(int32_t sw) const override { return mSwitches.count(sw); } + virtual bool hasForceFeedback(int32_t ff) const override { return mForceFeedbacks.count(ff); } + virtual bool hasInputProperty(int32_t property) const override { + return mInputProperties.count(property); + } + + // base case + void addKeys() {} + // inductive case + template + void addKeys(I key, Is... keys) { + // Add the first key + mKeys.insert(key); + // Recursively add the remaining keys + addKeys(keys...); + } + + void addRelAxis(int32_t axis) { mRelAxes.insert(axis); } + void addAbsAxis(int32_t axis, AbsoluteAxisInfo* info) { mAbsAxes[axis] = info; } + void addSwitch(int32_t sw) { mSwitches.insert(sw); } + void addForceFeedback(int32_t ff) { mForceFeedbacks.insert(ff); } + void addInputProperty(int32_t property) { mInputProperties.insert(property); } + + virtual int32_t getKeyState(int32_t key) const override { return 0; } + virtual int32_t getSwitchState(int32_t sw) const override { return 0; } + virtual const AbsoluteAxisInfo* getAbsoluteAxisInfo(int32_t axis) const override { + auto iter = mAbsAxes.find(axis); + if (iter != mAbsAxes.end()) { + return iter->second; + } + return nullptr; + } + virtual status_t getAbsoluteAxisValue(int32_t axis, int32_t* outValue) const override { + // TODO + return 0; + } + + virtual void vibrate(nsecs_t duration) override {} + virtual void cancelVibrate() override {} + + virtual void disableDriverKeyRepeat() override { mKeyRepeatDisabled = true; } + + bool isDriverKeyRepeatEnabled() { return mKeyRepeatDisabled; } + +private: + std::string mPath = "/test"; + std::string mName = "Test Device"; + std::string mLocation = "test/0"; + std::string mUniqueId = "test-id"; + + uint16_t mBusType = 0; + uint16_t mVendorId = 0; + uint16_t mProductId = 0; + uint16_t mVersion = 0; + + std::set mKeys; + std::set mRelAxes; + std::map mAbsAxes; + std::set mSwitches; + std::set mForceFeedbacks; + std::set mInputProperties; + + bool mKeyRepeatDisabled = false; +}; + +namespace MockNexus7v2 { +MockInputDeviceNode* getElanTouchscreen(); +MockInputDeviceNode* getLidInput(); +MockInputDeviceNode* getButtonJack(); +MockInputDeviceNode* getHeadsetJack(); +MockInputDeviceNode* getH2wButton(); +MockInputDeviceNode* getGpioKeys(); +} // namespace MockNexus7v2 + +namespace MockNexusPlayer { +MockInputDeviceNode* getGpioKeys(); +MockInputDeviceNode* getMidPowerBtn(); +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_ -- 2.11.0