Configure device classes for evdev devices.
Change-Id: Ia75b71253771d9d558c59411e27f8a51e352fb8b
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) {