OSDN Git Service

Configure device classes for evdev devices.
authorTim Kilbourn <tkilbourn@google.com>
Thu, 16 Apr 2015 22:32:08 +0000 (15:32 -0700)
committerTim Kilbourn <tkilbourn@google.com>
Mon, 1 Jun 2015 21:33:47 +0000 (14:33 -0700)
Change-Id: Ia75b71253771d9d558c59411e27f8a51e352fb8b

15 files changed:
modules/input/evdev/EvdevModule.cpp
modules/input/evdev/InputDevice.cpp
modules/input/evdev/InputDevice.h
modules/input/evdev/InputDeviceManager.cpp
modules/input/evdev/InputDeviceManager.h
modules/input/evdev/InputHost.cpp
modules/input/evdev/InputHost.h
modules/input/evdev/InputHub-internal.h [new file with mode: 0644]
modules/input/evdev/InputHub.cpp
modules/input/evdev/InputHub.h
tests/input/evdev/Android.mk
tests/input/evdev/InputDevice_test.cpp
tests/input/evdev/InputHub_test.cpp
tests/input/evdev/InputMocks.cpp [new file with mode: 0644]
tests/input/evdev/InputMocks.h [new file with mode: 0644]

index f6df219..1171a1a 100644 (file)
@@ -55,7 +55,7 @@ static std::unique_ptr<EvdevModule> gEvdevModule;
 
 EvdevModule::EvdevModule(InputHost inputHost) :
     mInputHost(inputHost),
-    mDeviceManager(std::make_shared<InputDeviceManager>()),
+    mDeviceManager(std::make_shared<InputDeviceManager>(mInputHost)),
     mInputHub(std::make_unique<InputHub>(mDeviceManager)) {}
 
 void EvdevModule::init() {
index 16f8039..883d6d4 100644 (file)
 #define LOG_TAG "InputDevice"
 #define LOG_NDEBUG 0
 
+// Enables debug output for processing input events
+#define DEBUG_INPUT_EVENTS 0
+
 #include <linux/input.h>
 
 #define __STDC_FORMAT_MACROS
 #include <cinttypes>
+#include <cstdlib>
 #include <string>
 
 #include <utils/Log.h>
 
 namespace android {
 
-EvdevDevice::EvdevDevice(const std::shared_ptr<InputDeviceNode>& node) :
-    mDeviceNode(node) {}
+static InputBus getInputBus(const std::shared_ptr<InputDeviceNode>& 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<InputDeviceNode>& 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) {
index 7a99f90..b4f3244 100644 (file)
@@ -21,6 +21,7 @@
 
 #include <utils/Timers.h>
 
+#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<InputDeviceNode>& node);
+    EvdevDevice(InputHost 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;
     std::shared_ptr<InputDeviceNode> 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_
index 79a9610..f21d6d1 100644 (file)
@@ -34,7 +34,7 @@ void InputDeviceManager::onInputEvent(const std::shared_ptr<InputDeviceNode>& no
 }
 
 void InputDeviceManager::onDeviceAdded(const std::shared_ptr<InputDeviceNode>& node) {
-    mDevices[node] = std::make_shared<EvdevDevice>(node);
+    mDevices[node] = std::make_shared<EvdevDevice>(mHost, node);
 }
 
 void InputDeviceManager::onDeviceRemoved(const std::shared_ptr<InputDeviceNode>& node) {
index 2c0ffc8..25dd912 100644 (file)
@@ -23,6 +23,7 @@
 #include <utils/Timers.h>
 
 #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<InputDeviceNode>& node, InputEvent& event,
@@ -42,6 +45,8 @@ public:
     virtual void onDeviceRemoved(const std::shared_ptr<InputDeviceNode>& node) override;
 
 private:
+    InputHost mHost;
+
     template<class T, class U>
     using DeviceMap = std::unordered_map<std::shared_ptr<T>, std::shared_ptr<U>>;
 
index 6a65fcd..74a5f8a 100644 (file)
@@ -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));
 }
index 98ce26f..d6a04d9 100644 (file)
@@ -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 (file)
index 0000000..b4f1297
--- /dev/null
@@ -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_
index ee64b29..8c48345 100644 (file)
  */
 
 #define LOG_TAG "InputHub"
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
+
+// Enables debug output for hasKeyInRange
+#define DEBUG_KEY_RANGE 0
 
 #include <dirent.h>
 #include <errno.h>
@@ -34,6 +37,7 @@
 #include <vector>
 
 #include "InputHub.h"
+#include "InputHub-internal.h"
 
 #include <android/input.h>
 #include <hardware_legacy/power.h>
@@ -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;
 
index dfab3db..1abdc09 100644 (file)
@@ -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:
index 167cbc2..544e5a8 100644 (file)
@@ -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 := \
index a96d664..123f2b8 100644 (file)
@@ -17,6 +17,8 @@
 #define LOG_TAG "InputHub_test"
 //#define LOG_NDEBUG 0
 
+#include <memory>
+
 #include <linux/input.h>
 
 #include <gtest/gtest.h>
@@ -24,7 +26,9 @@
 #include <utils/Timers.h>
 
 #include "InputDevice.h"
+#include "InputHost.h"
 #include "InputHub.h"
+#include "InputMocks.h"
 
 // # of milliseconds to allow for timing measurements
 #define TIMING_TOLERANCE_MS 25
 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<MockInputHost> mMockHost;
 };
 
-TEST(EvdevDeviceTest, testOverrideTime) {
+TEST_F(EvdevDeviceTest, testOverrideTime) {
+    InputHost host = {mMockHost.get(), kTestCallbacks};
     auto node = std::make_shared<MockInputDeviceNode>();
-    auto device = std::make_unique<EvdevDevice>(node);
+    auto device = std::make_unique<EvdevDevice>(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<MockInputDeviceNode>();
-    auto device = std::make_unique<EvdevDevice>(node);
+    auto device = std::make_unique<EvdevDevice>(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<MockInputDeviceNode>();
-    auto device = std::make_unique<EvdevDevice>(node);
+    auto device = std::make_unique<EvdevDevice>(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<MockInputDeviceNode>(MockNexus7v2::getElanTouchscreen());
+    auto device = std::make_unique<EvdevDevice>(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<MockInputDeviceNode>(MockNexus7v2::getButtonJack());
+    auto device = std::make_unique<EvdevDevice>(host, node);
+    EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
+}
+
+TEST_F(EvdevDeviceTest, testN7v2HeadsetJack) {
+    InputHost host = {mMockHost.get(), kTestCallbacks};
+    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getHeadsetJack());
+    auto device = std::make_unique<EvdevDevice>(host, 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);
+    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);
+    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);
+    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);
+    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);
+    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);
+    EXPECT_EQ(INPUT_DEVICE_CLASS_JOYSTICK|INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
+}
+
 }  // namespace tests
 }  // namespace android
index f2967b9..5864a95 100644 (file)
@@ -30,6 +30,7 @@
 #include <utils/Timers.h>
 
 #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 (file)
index 0000000..c316075
--- /dev/null
@@ -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<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
diff --git a/tests/input/evdev/InputMocks.h b/tests/input/evdev/InputMocks.h
new file mode 100644 (file)
index 0000000..5c6eb80
--- /dev/null
@@ -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 <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;
+    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<typename I, typename... Is>
+    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<int32_t> mKeys;
+    std::set<int32_t> mRelAxes;
+    std::map<int32_t, AbsoluteAxisInfo*> mAbsAxes;
+    std::set<int32_t> mSwitches;
+    std::set<int32_t> mForceFeedbacks;
+    std::set<int32_t> 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_