Configure device classes for evdev devices.

Change-Id: Ia75b71253771d9d558c59411e27f8a51e352fb8b
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 @@
 
 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() {
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 <linux/input.h>
 
 #define __STDC_FORMAT_MACROS
 #include <cinttypes>
+#include <cstdlib>
 #include <string>
 
 #include <utils/Log.h>
@@ -34,18 +38,177 @@
 
 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) {
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 <utils/Timers.h>
 
+#include "InputHost.h"
 #include "InputHub.h"
 
 namespace android {
@@ -33,6 +34,7 @@
 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 @@
  */
 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_
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::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) {
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 <utils/Timers.h>
 
 #include "InputDevice.h"
+#include "InputHost.h"
 #include "InputHub.h"
 
 namespace android {
@@ -34,6 +35,8 @@
  */
 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 @@
     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>>;
 
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 @@
     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 @@
     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 @@
     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 @@
 
     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 @@
 
     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 <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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
  */
 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: