am 87f61413: am 29b03118: am 1ce7fe6b: Merge "Fix DATA_INJECTION flag in sensors.h" into mnc-dr-dev

* commit '87f6141397bb0e0c58926e365d9292f7951727f8':
  Fix DATA_INJECTION flag in sensors.h
diff --git a/include/hardware/input.h b/include/hardware/input.h
index 969b8ce..c4a4cb5 100644
--- a/include/hardware/input.h
+++ b/include/hardware/input.h
@@ -318,6 +318,12 @@
     // axes
     INPUT_USAGE_AXIS_X,
     INPUT_USAGE_AXIS_Y,
+    INPUT_USAGE_AXIS_Z,
+    INPUT_USAGE_AXIS_RX,
+    INPUT_USAGE_AXIS_RY,
+    INPUT_USAGE_AXIS_RZ,
+    INPUT_USAGE_AXIS_HAT_X,
+    INPUT_USAGE_AXIS_HAT_Y,
     INPUT_USAGE_AXIS_PRESSURE,
     INPUT_USAGE_AXIS_SIZE,
     INPUT_USAGE_AXIS_TOUCH_MAJOR,
@@ -327,12 +333,6 @@
     INPUT_USAGE_AXIS_ORIENTATION,
     INPUT_USAGE_AXIS_VSCROLL,
     INPUT_USAGE_AXIS_HSCROLL,
-    INPUT_USAGE_AXIS_Z,
-    INPUT_USAGE_AXIS_RX,
-    INPUT_USAGE_AXIS_RY,
-    INPUT_USAGE_AXIS_RZ,
-    INPUT_USAGE_AXIS_HAT_X,
-    INPUT_USAGE_AXIS_HAT_Y,
     INPUT_USAGE_AXIS_LTRIGGER,
     INPUT_USAGE_AXIS_RTRIGGER,
     INPUT_USAGE_AXIS_THROTTLE,
@@ -375,13 +375,32 @@
     INPUT_USAGE_LED_CONTROLLER_2,
     INPUT_USAGE_LED_CONTROLLER_3,
     INPUT_USAGE_LED_CONTROLLER_4,
+
+    // switches
+    INPUT_USAGE_SWITCH_UNKNOWN,
+    INPUT_USAGE_SWITCH_LID,
+    INPUT_USAGE_SWITCH_KEYPAD_SLIDE,
+    INPUT_USAGE_SWITCH_HEADPHONE_INSERT,
+    INPUT_USAGE_SWITCH_MICROPHONE_INSERT,
+    INPUT_USAGE_SWITCH_LINEOUT_INSERT,
+    INPUT_USAGE_SWITCH_CAMERA_LENS_COVER,
+
+    // mouse buttons
+    // (see android.view.MotionEvent)
+    INPUT_USAGE_BUTTON_UNKNOWN,
+    INPUT_USAGE_BUTTON_PRIMARY,   // left
+    INPUT_USAGE_BUTTON_SECONDARY, // right
+    INPUT_USAGE_BUTTON_TERTIARY,  // middle
+    INPUT_USAGE_BUTTON_FORWARD,
+    INPUT_USAGE_BUTTON_BACK,
 } input_usage_t;
 
-typedef enum {
+typedef enum input_collection_id {
     INPUT_COLLECTION_ID_TOUCH,
     INPUT_COLLECTION_ID_KEYBOARD,
     INPUT_COLLECTION_ID_MOUSE,
     INPUT_COLLECTION_ID_TOUCHPAD,
+    INPUT_COLLECTION_ID_SWITCH,
     // etc
 } input_collection_id_t;
 
@@ -413,6 +432,11 @@
     input_report_definition_t* (*create_output_report_definition)(input_host_t* host);
 
     /**
+     * Frees the report definition.
+     */
+    void (*free_report_definition)(input_host_t* host, input_report_definition_t* report_def);
+
+    /**
      * Append the report to the given input device.
      */
     void (*input_device_definition_add_report)(input_host_t* host,
diff --git a/include/hardware/keymaster_defs.h b/include/hardware/keymaster_defs.h
index 5be956d..73d95d0 100644
--- a/include/hardware/keymaster_defs.h
+++ b/include/hardware/keymaster_defs.h
@@ -481,7 +481,8 @@
 #undef KEYMASTER_SIMPLE_COMPARE
 
 inline void keymaster_free_param_values(keymaster_key_param_t* param, size_t param_count) {
-    while (param_count-- > 0) {
+    while (param_count > 0) {
+        param_count--;
         switch (keymaster_tag_get_type(param->tag)) {
         case KM_BIGNUM:
         case KM_BYTES:
diff --git a/modules/input/evdev/Android.mk b/modules/input/evdev/Android.mk
index d3c49e7..9a5d092 100644
--- a/modules/input/evdev/Android.mk
+++ b/modules/input/evdev/Android.mk
@@ -18,10 +18,14 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := \
+    BitUtils.cpp \
     InputHub.cpp \
     InputDevice.cpp \
     InputDeviceManager.cpp \
-    InputHost.cpp
+    InputHost.cpp \
+    InputMapper.cpp \
+    MouseInputMapper.cpp \
+    SwitchInputMapper.cpp
 
 LOCAL_SHARED_LIBRARIES := \
     libhardware_legacy \
diff --git a/modules/input/evdev/BitUtils.cpp b/modules/input/evdev/BitUtils.cpp
new file mode 100644
index 0000000..3434c31
--- /dev/null
+++ b/modules/input/evdev/BitUtils.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BitUtils"
+//#define LOG_NDEBUG 0
+
+#include "BitUtils.h"
+
+#include <utils/Log.h>
+
+// Enables debug output for hasKeyInRange
+#define DEBUG_KEY_RANGE 0
+
+namespace android {
+
+#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 android
diff --git a/modules/input/evdev/BitUtils.h b/modules/input/evdev/BitUtils.h
new file mode 100644
index 0000000..1aa1f6a
--- /dev/null
+++ b/modules/input/evdev/BitUtils.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_BIT_UTILS_H_
+#define ANDROID_BIT_UTILS_H_
+
+#include <cstdint>
+
+namespace android {
+
+/** 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 android
+
+#endif  // ANDROID_BIT_UTILS_H_
diff --git a/modules/input/evdev/EvdevModule.cpp b/modules/input/evdev/EvdevModule.cpp
index e9c8222..93ccd35 100644
--- a/modules/input/evdev/EvdevModule.cpp
+++ b/modules/input/evdev/EvdevModule.cpp
@@ -37,7 +37,8 @@
 
 class EvdevModule {
 public:
-    explicit EvdevModule(InputHost inputHost);
+    // Takes ownership of the InputHostInterface
+    explicit EvdevModule(InputHostInterface* inputHost);
 
     void init();
     void notifyReport(input_report_t* r);
@@ -45,18 +46,18 @@
 private:
     void loop();
 
-    InputHost mInputHost;
+    std::unique_ptr<InputHostInterface> mInputHost;
     std::shared_ptr<InputDeviceManager> mDeviceManager;
-    std::shared_ptr<InputHub> mInputHub;
+    std::unique_ptr<InputHub> mInputHub;
     std::thread mPollThread;
 };
 
-static std::shared_ptr<EvdevModule> gEvdevModule;
+static std::unique_ptr<EvdevModule> gEvdevModule;
 
-EvdevModule::EvdevModule(InputHost inputHost) :
+EvdevModule::EvdevModule(InputHostInterface* inputHost) :
     mInputHost(inputHost),
-    mDeviceManager(std::make_shared<InputDeviceManager>()),
-    mInputHub(std::make_shared<InputHub>(mDeviceManager)) {}
+    mDeviceManager(std::make_shared<InputDeviceManager>(mInputHost.get())),
+    mInputHub(std::make_unique<InputHub>(mDeviceManager)) {}
 
 void EvdevModule::init() {
     ALOGV("%s", __func__);
@@ -97,8 +98,8 @@
 static void input_init(const input_module_t* module,
         input_host_t* host, input_host_callbacks_t cb) {
     LOG_ALWAYS_FATAL_IF(strcmp(module->common.id, INPUT_HARDWARE_MODULE_ID) != 0);
-    InputHost inputHost = {host, cb};
-    gEvdevModule = std::make_shared<EvdevModule>(inputHost);
+    auto inputHost = new InputHost(host, cb);
+    gEvdevModule = std::make_unique<EvdevModule>(inputHost);
     gEvdevModule->init();
 }
 
diff --git a/modules/input/evdev/InputDevice.cpp b/modules/input/evdev/InputDevice.cpp
index c0b59d7..b575117 100644
--- a/modules/input/evdev/InputDevice.cpp
+++ b/modules/input/evdev/InputDevice.cpp
@@ -17,35 +17,241 @@
 #define LOG_TAG "InputDevice"
 #define LOG_NDEBUG 0
 
+// Enables debug output for processing input events
+#define DEBUG_INPUT_EVENTS 0
+
+#include "InputDevice.h"
+
 #include <linux/input.h>
 
 #define __STDC_FORMAT_MACROS
 #include <cinttypes>
+#include <cstdlib>
 #include <string>
 
 #include <utils/Log.h>
 #include <utils/Timers.h>
 
+#include "InputHost.h"
 #include "InputHub.h"
-#include "InputDevice.h"
+#include "MouseInputMapper.h"
+#include "SwitchInputMapper.h"
 
 #define MSC_ANDROID_TIME_SEC  0x6
 #define MSC_ANDROID_TIME_USEC 0x7
 
 namespace android {
 
-EvdevDevice::EvdevDevice(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;
+}
+
+EvdevDevice::EvdevDevice(InputHostInterface* host, const std::shared_ptr<InputDeviceNode>& node) :
+    mHost(host), mDeviceNode(node), mDeviceDefinition(mHost->createDeviceDefinition()) {
+
+    InputBus bus = getInputBus(node);
+    mInputId = mHost->createDeviceIdentifier(
+            node->getName().c_str(),
+            node->getProductId(),
+            node->getVendorId(),
+            bus,
+            node->getUniqueId().c_str());
+
+    createMappers();
+    configureDevice();
+
+    // If we found a need for at least one mapper, register the device with the
+    // host. If there were no mappers, this device is effectively ignored, as
+    // the host won't know about it.
+    if (mMappers.size() > 0) {
+        mDeviceHandle = mHost->registerDevice(mInputId, mDeviceDefinition);
+        for (const auto& mapper : mMappers) {
+            mapper->setDeviceHandle(mDeviceHandle);
+        }
+    }
+}
+
+void EvdevDevice::createMappers() {
+    // See if this is a cursor device such as a trackball or mouse.
+    if (mDeviceNode->hasKey(BTN_MOUSE)
+            && mDeviceNode->hasRelativeAxis(REL_X)
+            && mDeviceNode->hasRelativeAxis(REL_Y)) {
+        mClasses |= INPUT_DEVICE_CLASS_CURSOR;
+        mMappers.push_back(std::make_unique<MouseInputMapper>());
+    }
+
+    bool isStylus = false;
+    bool haveGamepadButtons = mDeviceNode->hasKeyInRange(BTN_MISC, BTN_MOUSE) ||
+            mDeviceNode->hasKeyInRange(BTN_JOYSTICK, BTN_DIGI);
+
+    // See if this is a touch pad or stylus.
+    // Is this a new modern multi-touch driver?
+    if (mDeviceNode->hasAbsoluteAxis(ABS_MT_POSITION_X)
+            && mDeviceNode->hasAbsoluteAxis(ABS_MT_POSITION_Y)) {
+        // Some joysticks such as the PS3 controller report axes that conflict
+        // with the ABS_MT range. Try to confirm that the device really is a
+        // touch screen.
+        if (mDeviceNode->hasKey(BTN_TOUCH) || !haveGamepadButtons) {
+            mClasses |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
+            //mMappers.push_back(std::make_unique<MultiTouchInputMapper>());
+        }
+    // Is this an old style single-touch driver?
+    } else if (mDeviceNode->hasKey(BTN_TOUCH)
+            && mDeviceNode->hasAbsoluteAxis(ABS_X)
+            && mDeviceNode->hasAbsoluteAxis(ABS_Y)) {
+        mClasses |= INPUT_DEVICE_CLASS_TOUCH;
+        //mMappers.push_back(std::make_unique<SingleTouchInputMapper>());
+    // Is this a BT stylus?
+    } else if ((mDeviceNode->hasAbsoluteAxis(ABS_PRESSURE) || mDeviceNode->hasKey(BTN_TOUCH))
+            && !mDeviceNode->hasAbsoluteAxis(ABS_X) && !mDeviceNode->hasAbsoluteAxis(ABS_Y)) {
+        mClasses |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
+        //mMappers.push_back(std::make_unique<ExternalStylusInputMapper>());
+        isStylus = true;
+        mClasses &= ~INPUT_DEVICE_CLASS_KEYBOARD;
+    }
+
+    // See if this is a keyboard. Ignore everything in the button range except
+    // for joystick and gamepad buttons which are handled like keyboards for the
+    // most part.
+    // Keyboard will try to claim some of the stylus buttons but we really want
+    // to reserve those so we can fuse it with the touch screen data. Note this
+    // means an external stylus cannot also be a keyboard device.
+    if (!isStylus) {
+        bool haveKeyboardKeys = mDeviceNode->hasKeyInRange(0, BTN_MISC) ||
+            mDeviceNode->hasKeyInRange(KEY_OK, KEY_CNT);
+        if (haveKeyboardKeys || haveGamepadButtons) {
+            mClasses |= INPUT_DEVICE_CLASS_KEYBOARD;
+            //mMappers.push_back(std::make_unique<KeyboardInputMapper>());
+        }
+    }
+
+    // See if this device is a joystick.
+    // 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 = mClasses | INPUT_DEVICE_CLASS_JOYSTICK;
+        for (int i = 0; i < ABS_CNT; ++i) {
+            if (mDeviceNode->hasAbsoluteAxis(i)
+                    && getAbsAxisUsage(i, assumedClasses) == INPUT_DEVICE_CLASS_JOYSTICK) {
+                mClasses = assumedClasses;
+                //mMappers.push_back(std::make_unique<JoystickInputMapper>());
+                break;
+            }
+        }
+    }
+
+    // Check whether this device has switches.
+    for (int i = 0; i < SW_CNT; ++i) {
+        if (mDeviceNode->hasSwitch(i)) {
+            mClasses |= INPUT_DEVICE_CLASS_SWITCH;
+            mMappers.push_back(std::make_unique<SwitchInputMapper>());
+            break;
+        }
+    }
+
+    // Check whether this device supports the vibrator.
+    // TODO: decide if this is necessary.
+    if (mDeviceNode->hasForceFeedback(FF_RUMBLE)) {
+        mClasses |= INPUT_DEVICE_CLASS_VIBRATOR;
+        //mMappers.push_back(std::make_unique<VibratorInputMapper>());
+    }
+
+    ALOGD("device %s classes=0x%x %d mappers", mDeviceNode->getPath().c_str(), mClasses,
+            mMappers.size());
+}
+
+void EvdevDevice::configureDevice() {
+    for (const auto& mapper : mMappers) {
+        auto reportDef = mHost->createInputReportDefinition();
+        if (mapper->configureInputReport(mDeviceNode.get(), reportDef)) {
+            mDeviceDefinition->addReport(reportDef);
+        } else {
+            mHost->freeReportDefinition(reportDef);
+        }
+
+        reportDef = mHost->createOutputReportDefinition();
+        if (mapper->configureOutputReport(mDeviceNode.get(), reportDef)) {
+            mDeviceDefinition->addReport(reportDef);
+        } else {
+            mHost->freeReportDefinition(reportDef);
+        }
+    }
+}
 
 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) {
@@ -97,6 +303,10 @@
                     ", call time %" PRId64 ".", event.when, time, currentTime);
         }
     }
+
+    for (size_t i = 0; i < mMappers.size(); ++i) {
+        mMappers[i]->process(event);
+    }
 }
 
 }  // namespace android
diff --git a/modules/input/evdev/InputDevice.h b/modules/input/evdev/InputDevice.h
index 3aa16cc..6892778 100644
--- a/modules/input/evdev/InputDevice.h
+++ b/modules/input/evdev/InputDevice.h
@@ -18,13 +18,24 @@
 #define ANDROID_INPUT_DEVICE_H_
 
 #include <memory>
+#include <vector>
 
 #include <utils/Timers.h>
 
-#include "InputHub.h"
+#include "InputMapper.h"
+
+struct input_device_handle;
+struct input_device_identifier;
 
 namespace android {
 
+class InputDeviceDefinition;
+class InputDeviceNode;
+class InputHostInterface;
+struct InputEvent;
+using InputDeviceHandle = struct input_device_handle;
+using InputDeviceIdentifier = struct input_device_identifier;
+
 /**
  * InputDeviceInterface represents an input device in the HAL. It processes
  * input events before passing them to the input host.
@@ -33,6 +44,7 @@
 public:
     virtual void processInput(InputEvent& event, nsecs_t currentTime) = 0;
 
+    virtual uint32_t getInputClasses() = 0;
 protected:
     InputDeviceInterface() = default;
     virtual ~InputDeviceInterface() = default;
@@ -43,18 +55,75 @@
  */
 class EvdevDevice : public InputDeviceInterface {
 public:
-    explicit EvdevDevice(std::shared_ptr<InputDeviceNode> node);
+    EvdevDevice(InputHostInterface* host, const std::shared_ptr<InputDeviceNode>& node);
     virtual ~EvdevDevice() override = default;
 
     virtual void processInput(InputEvent& event, nsecs_t currentTime) override;
 
+    virtual uint32_t getInputClasses() override { return mClasses; }
 private:
+    void createMappers();
+    void configureDevice();
+
+    InputHostInterface* mHost = nullptr;
     std::shared_ptr<InputDeviceNode> mDeviceNode;
+    InputDeviceIdentifier* mInputId = nullptr;
+    InputDeviceDefinition* mDeviceDefinition = nullptr;
+    InputDeviceHandle* mDeviceHandle = nullptr;
+    std::vector<std::unique_ptr<InputMapper>> mMappers;
+    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 ceddd90..d50c1ae 100644
--- a/modules/input/evdev/InputDeviceManager.cpp
+++ b/modules/input/evdev/InputDeviceManager.cpp
@@ -17,14 +17,15 @@
 #define LOG_TAG "InputDeviceManager"
 //#define LOG_NDEBUG 0
 
+#include "InputDeviceManager.h"
+
 #include <utils/Log.h>
 
 #include "InputDevice.h"
-#include "InputDeviceManager.h"
 
 namespace android {
 
-void InputDeviceManager::onInputEvent(std::shared_ptr<InputDeviceNode> node, InputEvent& event,
+void InputDeviceManager::onInputEvent(const std::shared_ptr<InputDeviceNode>& node, InputEvent& event,
         nsecs_t event_time) {
     if (mDevices[node] == nullptr) {
         ALOGE("got input event for unknown node %s", node->getPath().c_str());
@@ -33,17 +34,18 @@
     mDevices[node]->processInput(event, event_time);
 }
 
-void InputDeviceManager::onDeviceAdded(std::shared_ptr<InputDeviceNode> node) {
-    mDevices[node] = std::make_shared<EvdevDevice>(node);
+void InputDeviceManager::onDeviceAdded(const std::shared_ptr<InputDeviceNode>& node) {
+    mDevices[node] = std::make_shared<EvdevDevice>(mHost, node);
 }
 
-void InputDeviceManager::onDeviceRemoved(std::shared_ptr<InputDeviceNode> node) {
+void InputDeviceManager::onDeviceRemoved(const std::shared_ptr<InputDeviceNode>& node) {
     if (mDevices[node] == nullptr) {
         ALOGE("could not remove unknown node %s", node->getPath().c_str());
         return;
     }
     // TODO: tell the InputDevice and InputDeviceNode that they are being
-    // removed so they can run any cleanup.
+    // removed so they can run any cleanup, including unregistering from the
+    // host.
     mDevices.erase(node);
 }
 
diff --git a/modules/input/evdev/InputDeviceManager.h b/modules/input/evdev/InputDeviceManager.h
index b652155..8fbf3ca 100644
--- a/modules/input/evdev/InputDeviceManager.h
+++ b/modules/input/evdev/InputDeviceManager.h
@@ -22,11 +22,13 @@
 
 #include <utils/Timers.h>
 
-#include "InputDevice.h"
 #include "InputHub.h"
 
 namespace android {
 
+class InputDeviceInterface;
+class InputHostInterface;
+
 /**
  * InputDeviceManager keeps the mapping of InputDeviceNodes to
  * InputDeviceInterfaces and handles the callbacks from the InputHub, delegating
@@ -34,14 +36,18 @@
  */
 class InputDeviceManager : public InputCallbackInterface {
 public:
+    explicit InputDeviceManager(InputHostInterface* host) :
+        mHost(host) {}
     virtual ~InputDeviceManager() override = default;
 
-    virtual void onInputEvent(std::shared_ptr<InputDeviceNode> node, InputEvent& event,
+    virtual void onInputEvent(const std::shared_ptr<InputDeviceNode>& node, InputEvent& event,
             nsecs_t event_time) override;
-    virtual void onDeviceAdded(std::shared_ptr<InputDeviceNode> node) override;
-    virtual void onDeviceRemoved(std::shared_ptr<InputDeviceNode> node) override;
+    virtual void onDeviceAdded(const std::shared_ptr<InputDeviceNode>& node) override;
+    virtual void onDeviceRemoved(const std::shared_ptr<InputDeviceNode>& node) override;
 
 private:
+    InputHostInterface* 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..5be4a79 100644
--- a/modules/input/evdev/InputHost.cpp
+++ b/modules/input/evdev/InputHost.cpp
@@ -18,7 +18,17 @@
 
 namespace android {
 
-void InputReport::reportEvent(InputDeviceHandle d) {
+void InputReport::setIntUsage(InputCollectionId id, InputUsage usage, int32_t value,
+        int32_t arityIndex) {
+    mCallbacks.input_report_set_usage_int(mHost, mReport, id, usage, value, arityIndex);
+}
+
+void InputReport::setBoolUsage(InputCollectionId id, InputUsage usage, bool value,
+        int32_t arityIndex) {
+    mCallbacks.input_report_set_usage_bool(mHost, mReport, id, usage, value, arityIndex);
+}
+
+void InputReport::reportEvent(InputDeviceHandle* d) {
     mCallbacks.report_event(mHost, d, mReport);
 }
 
@@ -32,73 +42,78 @@
             id, usage, min, max, resolution);
 }
 
-void InputReportDefinition::declareUsage(InputCollectionId id, InputUsage* usage,
+void InputReportDefinition::declareUsages(InputCollectionId id, InputUsage* usage,
         size_t usageCount) {
     mCallbacks.input_report_definition_declare_usages_bool(mHost, mReportDefinition,
             id, usage, usageCount);
 }
 
-InputReport InputReportDefinition::allocateReport() {
-    return InputReport(mHost, mCallbacks,
+InputReport* InputReportDefinition::allocateReport() {
+    return new InputReport(mHost, mCallbacks,
             mCallbacks.input_allocate_report(mHost, mReportDefinition));
 }
 
-void InputDeviceDefinition::addReport(InputReportDefinition r) {
-    mCallbacks.input_device_definition_add_report(mHost, mDeviceDefinition, r);
+void InputDeviceDefinition::addReport(InputReportDefinition* r) {
+    mCallbacks.input_device_definition_add_report(mHost, mDeviceDefinition, *r);
 }
 
-InputProperty::~InputProperty() {
-    mCallbacks.input_free_device_property(mHost, mProperty);
-}
-
-const char* InputProperty::getKey() {
+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);
 }
 
-InputPropertyMap::~InputPropertyMap() {
-    mCallbacks.input_free_device_property_map(mHost, mMap);
-}
-
-InputProperty InputPropertyMap::getDeviceProperty(const char* key) {
-    return InputProperty(mHost, mCallbacks,
+InputProperty* InputPropertyMap::getDeviceProperty(const char* key) const {
+    return new InputProperty(mHost, mCallbacks,
             mCallbacks.input_get_device_property(mHost, mMap, key));
 }
 
-InputDeviceIdentifier InputHost::createDeviceIdentifier(const char* name, int32_t productId,
+void InputPropertyMap::freeDeviceProperty(InputProperty* property) const {
+    mCallbacks.input_free_device_property(mHost, *property);
+}
+
+InputDeviceIdentifier* InputHost::createDeviceIdentifier(const char* name, int32_t productId,
         int32_t vendorId, InputBus bus, const char* uniqueId) {
-    return mCallbacks.create_device_identifier(mHost, name, productId, vendorId, bus, uniqueId);
+    return mCallbacks.create_device_identifier(
+                mHost, name, productId, vendorId, bus, uniqueId);
 }
 
-InputDeviceDefinition InputHost::createDeviceDefinition() {
-    return InputDeviceDefinition(mHost, mCallbacks, mCallbacks.create_device_definition(mHost));
+InputDeviceDefinition* InputHost::createDeviceDefinition() {
+    return new InputDeviceDefinition(mHost, mCallbacks, mCallbacks.create_device_definition(mHost));
 }
 
-InputReportDefinition InputHost::createInputReportDefinition() {
-    return InputReportDefinition(mHost, mCallbacks,
+InputReportDefinition* InputHost::createInputReportDefinition() {
+    return new InputReportDefinition(mHost, mCallbacks,
             mCallbacks.create_input_report_definition(mHost));
 }
 
-InputReportDefinition InputHost::createOutputReportDefinition() {
-    return InputReportDefinition(mHost, mCallbacks,
+InputReportDefinition* InputHost::createOutputReportDefinition() {
+    return new InputReportDefinition(mHost, mCallbacks,
             mCallbacks.create_output_report_definition(mHost));
 }
 
-InputDeviceHandle InputHost::registerDevice(InputDeviceIdentifier id,
-        InputDeviceDefinition d) {
-    return mCallbacks.register_device(mHost, id, d);
+void InputHost::freeReportDefinition(InputReportDefinition* reportDef) {
+    mCallbacks.free_report_definition(mHost, *reportDef);
 }
 
-void InputHost::unregisterDevice(InputDeviceHandle handle) {
-    return mCallbacks.unregister_device(mHost, handle);
+InputDeviceHandle* InputHost::registerDevice(InputDeviceIdentifier* id,
+        InputDeviceDefinition* d) {
+    return mCallbacks.register_device(mHost, id, *d);
 }
 
-InputPropertyMap InputHost::getDevicePropertyMap(InputDeviceIdentifier id) {
-    return InputPropertyMap(mHost, mCallbacks,
+void InputHost::unregisterDevice(InputDeviceHandle* handle) {
+    mCallbacks.unregister_device(mHost, handle);
+}
+
+InputPropertyMap* InputHost::getDevicePropertyMap(InputDeviceIdentifier* id) {
+    return new InputPropertyMap(mHost, mCallbacks,
             mCallbacks.input_get_device_property_map(mHost, id));
 }
 
+void InputHost::freeDevicePropertyMap(InputPropertyMap* propertyMap) {
+    mCallbacks.input_free_device_property_map(mHost, *propertyMap);
+}
+
 }  // namespace android
diff --git a/modules/input/evdev/InputHost.h b/modules/input/evdev/InputHost.h
index 98ce26f..d6573d2 100644
--- a/modules/input/evdev/InputHost.h
+++ b/modules/input/evdev/InputHost.h
@@ -25,17 +25,17 @@
 
 /**
  * Classes in this file wrap the corresponding interfaces in the Input HAL. They
- * are intended to be lightweight and passed by value. It is still important not
- * to use an object after a HAL-specific method has freed the underlying
- * representation.
+ * are intended to be lightweight, as they primarily wrap pointers to callbacks.
+ * It is still important not to use an object after a HAL-specific method has
+ * freed the underlying representation.
  *
  * See hardware/input.h for details about each of these methods.
  */
 
 using InputBus = input_bus_t;
 using InputCollectionId = input_collection_id_t;
-using InputDeviceHandle = input_device_handle_t*;
-using InputDeviceIdentifier = input_device_identifier_t*;
+using InputDeviceHandle = input_device_handle_t;
+using InputDeviceIdentifier = input_device_identifier_t;
 using InputUsage = input_usage_t;
 
 class InputHostBase {
@@ -43,148 +43,148 @@
     InputHostBase(input_host_t* host, input_host_callbacks_t cb) : mHost(host), mCallbacks(cb) {}
     virtual ~InputHostBase() = default;
 
+    InputHostBase(const InputHostBase& rhs) = delete;
+    InputHostBase(InputHostBase&& rhs) = delete;
+
     input_host_t* mHost;
     input_host_callbacks_t mCallbacks;
 };
 
 class InputReport : private InputHostBase {
 public:
-    virtual ~InputReport() = default;
-
-    InputReport(const InputReport& rhs) = default;
-    InputReport& operator=(const InputReport& rhs) = default;
-    operator input_report_t*() const { return mReport; }
-
-    void reportEvent(InputDeviceHandle d);
-
-private:
-    friend class InputReportDefinition;
-
     InputReport(input_host_t* host, input_host_callbacks_t cb, input_report_t* r) :
         InputHostBase(host, cb), mReport(r) {}
+    virtual ~InputReport() = default;
 
+    virtual void setIntUsage(InputCollectionId id, InputUsage usage, int32_t value,
+            int32_t arityIndex);
+    virtual void setBoolUsage(InputCollectionId id, InputUsage usage, bool value,
+            int32_t arityIndex);
+    virtual void reportEvent(InputDeviceHandle* d);
+
+    operator input_report_t*() const { return mReport; }
+
+    InputReport(const InputReport& rhs) = delete;
+    InputReport& operator=(const InputReport& rhs) = delete;
+private:
     input_report_t* mReport;
 };
 
 class InputReportDefinition : private InputHostBase {
 public:
+    InputReportDefinition(input_host_t* host, input_host_callbacks_t cb,
+            input_report_definition_t* r) : InputHostBase(host, cb), mReportDefinition(r) {}
     virtual ~InputReportDefinition() = default;
 
-    InputReportDefinition(const InputReportDefinition& rhs) = default;
-    InputReportDefinition& operator=(const InputReportDefinition& rhs) = default;
+    virtual void addCollection(InputCollectionId id, int32_t arity);
+    virtual void declareUsage(InputCollectionId id, InputUsage usage, int32_t min, int32_t max,
+            float resolution);
+    virtual void declareUsages(InputCollectionId id, InputUsage* usage, size_t usageCount);
+
+    virtual InputReport* allocateReport();
+
     operator input_report_definition_t*() { return mReportDefinition; }
 
-    void addCollection(InputCollectionId id, int32_t arity);
-    void declareUsage(InputCollectionId id, InputUsage usage, int32_t min, int32_t max,
-            float resolution);
-    void declareUsage(InputCollectionId id, InputUsage* usage, size_t usageCount);
-
-    InputReport allocateReport();
-
+    InputReportDefinition(const InputReportDefinition& rhs) = delete;
+    InputReportDefinition& operator=(const InputReportDefinition& rhs) = delete;
 private:
-    friend class InputHost;
-
-    InputReportDefinition(
-            input_host_t* host, input_host_callbacks_t cb, input_report_definition_t* r) :
-        InputHostBase(host, cb), mReportDefinition(r) {}
-
     input_report_definition_t* mReportDefinition;
 };
 
 class InputDeviceDefinition : private InputHostBase {
 public:
+    InputDeviceDefinition(input_host_t* host, input_host_callbacks_t cb,
+            input_device_definition_t* d) :
+        InputHostBase(host, cb), mDeviceDefinition(d) {}
     virtual ~InputDeviceDefinition() = default;
 
-    InputDeviceDefinition(const InputDeviceDefinition& rhs) = default;
-    InputDeviceDefinition& operator=(const InputDeviceDefinition& rhs) = default;
+    virtual void addReport(InputReportDefinition* r);
+
     operator input_device_definition_t*() { return mDeviceDefinition; }
 
-    void addReport(InputReportDefinition r);
-
+    InputDeviceDefinition(const InputDeviceDefinition& rhs) = delete;
+    InputDeviceDefinition& operator=(const InputDeviceDefinition& rhs) = delete;
 private:
-    friend class InputHost;
-
-    InputDeviceDefinition(
-            input_host_t* host, input_host_callbacks_t cb, input_device_definition_t* d) :
-        InputHostBase(host, cb), mDeviceDefinition(d) {}
-
     input_device_definition_t* mDeviceDefinition;
 };
 
 class InputProperty : private InputHostBase {
 public:
-    virtual ~InputProperty();
+    virtual ~InputProperty() = default;
+
+    InputProperty(input_host_t* host, input_host_callbacks_t cb, input_property_t* p) :
+        InputHostBase(host, cb), mProperty(p) {}
+
+    virtual const char* getKey() const;
+    virtual const char* getValue() const;
 
     operator input_property_t*() { return mProperty; }
 
-    const char* getKey();
-    const char* getValue();
-
-    // Default move constructor transfers ownership of the input_property_t
-    // pointer.
-    InputProperty(InputProperty&& rhs) = default;
-
-    // Prevent copy/assign because of the ownership of the underlying
-    // input_property_t pointer.
     InputProperty(const InputProperty& rhs) = delete;
     InputProperty& operator=(const InputProperty& rhs) = delete;
-
 private:
-    friend class InputPropertyMap;
-
-    InputProperty(
-            input_host_t* host, input_host_callbacks_t cb, input_property_t* p) :
-        InputHostBase(host, cb), mProperty(p) {}
-
     input_property_t* mProperty;
 };
 
 class InputPropertyMap : private InputHostBase {
 public:
-    virtual ~InputPropertyMap();
+    virtual ~InputPropertyMap() = default;
+
+    InputPropertyMap(input_host_t* host, input_host_callbacks_t cb, input_property_map_t* m) :
+        InputHostBase(host, cb), mMap(m) {}
+
+    virtual InputProperty* getDeviceProperty(const char* key) const;
+    virtual void freeDeviceProperty(InputProperty* property) const;
 
     operator input_property_map_t*() { return mMap; }
 
-    InputProperty getDeviceProperty(const char* key);
-
-    // Default move constructor transfers ownership of the input_property_map_t
-    // pointer.
-    InputPropertyMap(InputPropertyMap&& rhs) = default;
-
-    // Prevent copy/assign because of the ownership of the underlying
-    // input_property_map_t pointer.
     InputPropertyMap(const InputPropertyMap& rhs) = delete;
     InputPropertyMap& operator=(const InputPropertyMap& rhs) = delete;
-
 private:
-    friend class InputHost;
-
-    InputPropertyMap(
-            input_host_t* host, input_host_callbacks_t cb, input_property_map_t* m) :
-        InputHostBase(host, cb), mMap(m) {}
-
     input_property_map_t* mMap;
 };
 
-class InputHost : private InputHostBase {
+class InputHostInterface {
+public:
+    virtual ~InputHostInterface() = default;
+
+    virtual InputDeviceIdentifier* createDeviceIdentifier(const char* name, int32_t productId,
+            int32_t vendorId, InputBus bus, const char* uniqueId) = 0;
+
+    virtual InputDeviceDefinition* createDeviceDefinition() = 0;
+    virtual InputReportDefinition* createInputReportDefinition() = 0;
+    virtual InputReportDefinition* createOutputReportDefinition() = 0;
+    virtual void freeReportDefinition(InputReportDefinition* reportDef) = 0;
+
+    virtual InputDeviceHandle* registerDevice(InputDeviceIdentifier* id,
+            InputDeviceDefinition* d) = 0;
+    virtual void unregisterDevice(InputDeviceHandle* handle) = 0;
+
+    virtual InputPropertyMap* getDevicePropertyMap(InputDeviceIdentifier* id) = 0;
+    virtual void freeDevicePropertyMap(InputPropertyMap* propertyMap) = 0;
+};
+
+class InputHost : public InputHostInterface, private InputHostBase {
 public:
     InputHost(input_host_t* host, input_host_callbacks_t cb) : InputHostBase(host, cb) {}
     virtual ~InputHost() = default;
 
-    InputHost(const InputHost& rhs) = default;
-    InputHost& operator=(const InputHost& rhs) = default;
+    InputDeviceIdentifier* createDeviceIdentifier(const char* name, int32_t productId,
+            int32_t vendorId, InputBus bus, const char* uniqueId) override;
 
-    InputDeviceIdentifier createDeviceIdentifier(const char* name, int32_t productId,
-            int32_t vendorId, InputBus bus, const char* uniqueId);
+    InputDeviceDefinition* createDeviceDefinition() override;
+    InputReportDefinition* createInputReportDefinition() override;
+    InputReportDefinition* createOutputReportDefinition() override;
+    virtual void freeReportDefinition(InputReportDefinition* reportDef) override;
 
-    InputDeviceDefinition createDeviceDefinition();
-    InputReportDefinition createInputReportDefinition();
-    InputReportDefinition createOutputReportDefinition();
+    InputDeviceHandle* registerDevice(InputDeviceIdentifier* id, InputDeviceDefinition* d) override;
+    void unregisterDevice(InputDeviceHandle* handle) override;
 
-    InputDeviceHandle registerDevice(InputDeviceIdentifier id, InputDeviceDefinition d);
-    void unregisterDevice(InputDeviceHandle handle);
+    InputPropertyMap* getDevicePropertyMap(InputDeviceIdentifier* id) override;
+    void freeDevicePropertyMap(InputPropertyMap* propertyMap) override;
 
-    InputPropertyMap getDevicePropertyMap(InputDeviceIdentifier id);
+    InputHost(const InputHost& rhs) = delete;
+    InputHost& operator=(const InputHost& rhs) = delete;
 };
 
 }  // namespace android
diff --git a/modules/input/evdev/InputHub.cpp b/modules/input/evdev/InputHub.cpp
index e72ac2e..389955d 100644
--- a/modules/input/evdev/InputHub.cpp
+++ b/modules/input/evdev/InputHub.cpp
@@ -15,7 +15,9 @@
  */
 
 #define LOG_TAG "InputHub"
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
+
+#include "InputHub.h"
 
 #include <dirent.h>
 #include <errno.h>
@@ -33,14 +35,14 @@
 
 #include <vector>
 
-#include "InputHub.h"
-
 #include <android/input.h>
 #include <hardware_legacy/power.h>
 #include <linux/input.h>
 
 #include <utils/Log.h>
 
+#include "BitUtils.h"
+
 namespace android {
 
 static const char WAKE_LOCK_ID[] = "KeyEvents";
@@ -74,7 +76,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 +103,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 +277,10 @@
     return false;
 }
 
+bool EvdevDeviceNode::hasKeyInRange(int32_t startKey, int32_t endKey) const {
+    return testBitInRange(mKeyBitmask, startKey, endKey);
+}
+
 bool EvdevDeviceNode::hasRelativeAxis(int axis) const {
     if (axis >= 0 && axis <= REL_MAX) {
         return testBit(axis, mRelBitmask);
@@ -279,6 +288,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 +307,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 +401,7 @@
     mFfEffectPlaying = true;
 }
 
-void EvdevDeviceNode::cancelVibrate(int32_t deviceId) {
+void EvdevDeviceNode::cancelVibrate() {
     if (mFfEffectPlaying) {
         mFfEffectPlaying = false;
 
@@ -396,7 +426,7 @@
     }
 }
 
-InputHub::InputHub(std::shared_ptr<InputCallbackInterface> cb) :
+InputHub::InputHub(const std::shared_ptr<InputCallbackInterface>& cb) :
     mInputCallback(cb) {
     // Determine the type of suspend blocking we can do on this device. There
     // are 3 options, in decreasing order of preference:
@@ -670,9 +700,8 @@
             ALOGV("inotify event for path %s", path.c_str());
 
             if (event->mask & IN_CREATE) {
-                std::shared_ptr<InputDeviceNode> deviceNode;
-                status_t res = openNode(path, &deviceNode);
-                if (res != OK) {
+                auto deviceNode = openNode(path);
+                if (deviceNode == nullptr) {
                     ALOGE("could not open device node %s. err=%d", path.c_str(), res);
                 } else {
                     mInputCallback->onDeviceAdded(deviceNode);
@@ -680,7 +709,7 @@
             } else {
                 auto deviceNode = findNodeByPath(path);
                 if (deviceNode != nullptr) {
-                    status_t ret = closeNode(deviceNode);
+                    status_t ret = closeNode(deviceNode.get());
                     if (ret != OK) {
                         ALOGW("Could not close device %s. errno=%d", path.c_str(), ret);
                     } else {
@@ -712,8 +741,8 @@
             continue;
         }
         std::string filename = path + "/" + dirent->d_name;
-        std::shared_ptr<InputDeviceNode> node;
-        if (openNode(filename, &node) != OK) {
+        auto node = openNode(filename);
+        if (node == nullptr) {
             ALOGE("could not open device node %s", filename.c_str());
         } else {
             mInputCallback->onDeviceAdded(node);
@@ -723,18 +752,16 @@
     return OK;
 }
 
-status_t InputHub::openNode(const std::string& path,
-        std::shared_ptr<InputDeviceNode>* outNode) {
+std::shared_ptr<InputDeviceNode> InputHub::openNode(const std::string& path) {
     ALOGV("opening %s...", path.c_str());
     auto evdevNode = std::shared_ptr<EvdevDeviceNode>(EvdevDeviceNode::openDeviceNode(path));
     if (evdevNode == nullptr) {
-        return UNKNOWN_ERROR;
+        return nullptr;
     }
 
     auto fd = evdevNode->getFd();
     ALOGV("opened %s with fd %d", path.c_str(), fd);
-    *outNode = std::static_pointer_cast<InputDeviceNode>(evdevNode);
-    mDeviceNodes[fd] = *outNode;
+    mDeviceNodes[fd] = evdevNode;
     struct epoll_event eventItem{};
     eventItem.events = EPOLLIN;
     if (mWakeupMechanism == WakeMechanism::EPOLL_WAKEUP) {
@@ -743,7 +770,7 @@
     eventItem.data.u32 = fd;
     if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) {
         ALOGE("Could not add device fd to epoll instance. errno=%d", errno);
-        return -errno;
+        return nullptr;
     }
 
     if (mNeedToCheckSuspendBlockIoctl) {
@@ -765,12 +792,12 @@
         mNeedToCheckSuspendBlockIoctl = false;
     }
 
-    return OK;
+    return evdevNode;
 }
 
-status_t InputHub::closeNode(const std::shared_ptr<InputDeviceNode>& node) {
+status_t InputHub::closeNode(const InputDeviceNode* node) {
     for (auto pair : mDeviceNodes) {
-        if (pair.second.get() == node.get()) {
+        if (pair.second.get() == node) {
             return closeNodeByFd(pair.first);
         }
     }
diff --git a/modules/input/evdev/InputHub.h b/modules/input/evdev/InputHub.h
index bec327a..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:
@@ -89,10 +115,10 @@
 /** Callback interface for receiving input events, including device changes. */
 class InputCallbackInterface {
 public:
-    virtual void onInputEvent(std::shared_ptr<InputDeviceNode> node, InputEvent& event,
+    virtual void onInputEvent(const std::shared_ptr<InputDeviceNode>& node, InputEvent& event,
             nsecs_t event_time) = 0;
-    virtual void onDeviceAdded(std::shared_ptr<InputDeviceNode> node) = 0;
-    virtual void onDeviceRemoved(std::shared_ptr<InputDeviceNode> node) = 0;
+    virtual void onDeviceAdded(const std::shared_ptr<InputDeviceNode>& node) = 0;
+    virtual void onDeviceRemoved(const std::shared_ptr<InputDeviceNode>& node) = 0;
 
 protected:
     InputCallbackInterface() = default;
@@ -129,7 +155,7 @@
  */
 class InputHub : public InputHubInterface {
 public:
-    explicit InputHub(std::shared_ptr<InputCallbackInterface> cb);
+    explicit InputHub(const std::shared_ptr<InputCallbackInterface>& cb);
     virtual ~InputHub() override;
 
     virtual status_t registerDevicePath(const std::string& path) override;
@@ -143,8 +169,8 @@
 private:
     status_t readNotify();
     status_t scanDir(const std::string& path);
-    status_t openNode(const std::string& path, std::shared_ptr<InputDeviceNode>* outNode);
-    status_t closeNode(const std::shared_ptr<InputDeviceNode>& node);
+    std::shared_ptr<InputDeviceNode> openNode(const std::string& path);
+    status_t closeNode(const InputDeviceNode* node);
     status_t closeNodeByFd(int fd);
     std::shared_ptr<InputDeviceNode> findNodeByPath(const std::string& path);
 
diff --git a/modules/input/evdev/InputMapper.cpp b/modules/input/evdev/InputMapper.cpp
new file mode 100644
index 0000000..3893125
--- /dev/null
+++ b/modules/input/evdev/InputMapper.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InputMapper.h"
+
+#include "InputHost.h"
+
+namespace android {
+
+InputReport* InputMapper::getInputReport() {
+    if (mReport) return mReport;
+    if (mInputReportDef == nullptr) return nullptr;
+    mReport = mInputReportDef->allocateReport();
+    return mReport;
+}
+
+}  // namespace android
diff --git a/modules/input/evdev/InputMapper.h b/modules/input/evdev/InputMapper.h
new file mode 100644
index 0000000..5e88d06
--- /dev/null
+++ b/modules/input/evdev/InputMapper.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_INPUT_MAPPER_H_
+#define ANDROID_INPUT_MAPPER_H_
+
+struct input_device_handle;
+
+namespace android {
+
+class InputDeviceNode;
+class InputReport;
+class InputReportDefinition;
+struct InputEvent;
+using InputDeviceHandle = struct input_device_handle;
+
+/**
+ * An InputMapper processes raw evdev input events and combines them into
+ * Android input HAL reports. A given InputMapper will focus on a particular
+ * type of input, like key presses or touch events. A single InputDevice may
+ * have multiple InputMappers, corresponding to the different types of inputs it
+ * supports.
+ */
+class InputMapper {
+public:
+    InputMapper() = default;
+    virtual ~InputMapper() {}
+
+    /**
+     * If the mapper supports input events from the InputDevice,
+     * configureInputReport will populate the InputReportDefinition and return
+     * true. If input is not supported, false is returned, and the InputDevice
+     * may free or re-use the InputReportDefinition.
+     */
+    virtual bool configureInputReport(InputDeviceNode* devNode, InputReportDefinition* report) {
+        return false;
+    }
+
+    /**
+     * If the mapper supports output events from the InputDevice,
+     * configureOutputReport will populate the InputReportDefinition and return
+     * true. If output is not supported, false is returned, and the InputDevice
+     * may free or re-use the InputReportDefinition.
+     */
+    virtual bool configureOutputReport(InputDeviceNode* devNode, InputReportDefinition* report) {
+        return false;
+    }
+
+    // Set the InputDeviceHandle after registering the device with the host.
+    virtual void setDeviceHandle(InputDeviceHandle* handle) { mDeviceHandle = handle; }
+    // Process the InputEvent.
+    virtual void process(const InputEvent& event) = 0;
+
+protected:
+    virtual void setInputReportDefinition(InputReportDefinition* reportDef) final {
+        mInputReportDef = reportDef;
+    }
+    virtual void setOutputReportDefinition(InputReportDefinition* reportDef) final {
+        mOutputReportDef = reportDef;
+    }
+    virtual InputReportDefinition* getInputReportDefinition() final { return mInputReportDef; }
+    virtual InputReportDefinition* getOutputReportDefinition() final { return mOutputReportDef; }
+    virtual InputDeviceHandle* getDeviceHandle() final { return mDeviceHandle; }
+    virtual InputReport* getInputReport() final;
+
+private:
+    InputReportDefinition* mInputReportDef = nullptr;
+    InputReportDefinition* mOutputReportDef = nullptr;
+    InputDeviceHandle* mDeviceHandle = nullptr;
+    InputReport* mReport = nullptr;
+};
+
+}  // namespace android
+
+#endif  // ANDROID_INPUT_MAPPER_H_
diff --git a/modules/input/evdev/MouseInputMapper.cpp b/modules/input/evdev/MouseInputMapper.cpp
new file mode 100644
index 0000000..453edde
--- /dev/null
+++ b/modules/input/evdev/MouseInputMapper.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MouseInputMapper"
+//#define LOG_NDEBUG 0
+
+#include "MouseInputMapper.h"
+
+#include <linux/input.h>
+#include <hardware/input.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+#include "InputHost.h"
+#include "InputHub.h"
+
+
+namespace android {
+
+// Map scancodes to input HAL usages.
+// The order of these definitions MUST remain in sync with the order they are
+// defined in linux/input.h.
+static struct {
+    int32_t scancode;
+    InputUsage usage;
+} codeMap[] = {
+    {BTN_LEFT, INPUT_USAGE_BUTTON_PRIMARY},
+    {BTN_RIGHT, INPUT_USAGE_BUTTON_SECONDARY},
+    {BTN_MIDDLE, INPUT_USAGE_BUTTON_TERTIARY},
+    {BTN_SIDE, INPUT_USAGE_BUTTON_UNKNOWN},
+    {BTN_EXTRA, INPUT_USAGE_BUTTON_UNKNOWN},
+    {BTN_FORWARD, INPUT_USAGE_BUTTON_FORWARD},
+    {BTN_BACK, INPUT_USAGE_BUTTON_BACK},
+    {BTN_TASK, INPUT_USAGE_BUTTON_UNKNOWN},
+};
+
+
+bool MouseInputMapper::configureInputReport(InputDeviceNode* devNode,
+        InputReportDefinition* report) {
+    setInputReportDefinition(report);
+    getInputReportDefinition()->addCollection(INPUT_COLLECTION_ID_MOUSE, 1);
+
+    // Configure mouse axes
+    if (!devNode->hasRelativeAxis(REL_X) || !devNode->hasRelativeAxis(REL_Y)) {
+        ALOGE("Device %s is missing a relative x or y axis. Device cannot be configured.",
+                devNode->getPath().c_str());
+        return false;
+    }
+    getInputReportDefinition()->declareUsage(INPUT_COLLECTION_ID_MOUSE, INPUT_USAGE_AXIS_X,
+            INT32_MIN, INT32_MAX, 1.0f);
+    getInputReportDefinition()->declareUsage(INPUT_COLLECTION_ID_MOUSE, INPUT_USAGE_AXIS_Y,
+            INT32_MIN, INT32_MAX, 1.0f);
+    if (devNode->hasRelativeAxis(REL_WHEEL)) {
+        getInputReportDefinition()->declareUsage(INPUT_COLLECTION_ID_MOUSE,
+                INPUT_USAGE_AXIS_VSCROLL, -1, 1, 0.0f);
+    }
+    if (devNode->hasRelativeAxis(REL_HWHEEL)) {
+        getInputReportDefinition()->declareUsage(INPUT_COLLECTION_ID_MOUSE,
+                INPUT_USAGE_AXIS_HSCROLL, -1, 1, 0.0f);
+    }
+
+    // Configure mouse buttons
+    InputUsage usages[NELEM(codeMap)];
+    int numUsages = 0;
+    for (int32_t i = 0; i < NELEM(codeMap); ++i) {
+        if (devNode->hasKey(codeMap[i].scancode)) {
+            usages[numUsages++] = codeMap[i].usage;
+        }
+    }
+    if (numUsages == 0) {
+        ALOGW("MouseInputMapper found no buttons for %s", devNode->getPath().c_str());
+    }
+    getInputReportDefinition()->declareUsages(INPUT_COLLECTION_ID_MOUSE, usages, numUsages);
+    return true;
+}
+
+void MouseInputMapper::process(const InputEvent& event) {
+    ALOGD("processing mouse event. type=%d code=%d value=%d",
+            event.type, event.code, event.value);
+    switch (event.type) {
+        case EV_KEY:
+            processButton(event.code, event.value);
+            break;
+        case EV_REL:
+            processMotion(event.code, event.value);
+            break;
+        case EV_SYN:
+            if (event.code == SYN_REPORT) {
+                sync(event.when);
+            }
+            break;
+        default:
+            ALOGD("unknown mouse event type: %d", event.type);
+    }
+}
+
+void MouseInputMapper::processMotion(int32_t code, int32_t value) {
+    switch (code) {
+        case REL_X:
+            mRelX = value;
+            break;
+        case REL_Y:
+            mRelY = value;
+            break;
+        case REL_WHEEL:
+            mRelWheel = value;
+            break;
+        case REL_HWHEEL:
+            mRelHWheel = value;
+            break;
+        default:
+            // Unknown code. Ignore.
+            break;
+    }
+}
+
+// Map evdev button codes to bit indices. This function assumes code >=
+// BTN_MOUSE.
+uint32_t buttonToBit(int32_t code) {
+    return static_cast<uint32_t>(code - BTN_MOUSE);
+}
+
+void MouseInputMapper::processButton(int32_t code, int32_t value) {
+    // Mouse buttons start at BTN_MOUSE and end before BTN_JOYSTICK. There isn't
+    // really enough room after the mouse buttons for another button class, so
+    // the risk of a button type being inserted after mouse is low.
+    if (code >= BTN_MOUSE && code < BTN_JOYSTICK) {
+        if (value) {
+            mButtonValues.markBit(buttonToBit(code));
+        } else {
+            mButtonValues.clearBit(buttonToBit(code));
+        }
+        mUpdatedButtonMask.markBit(buttonToBit(code));
+    }
+}
+
+void MouseInputMapper::sync(nsecs_t when) {
+    // Process updated button states.
+    while (!mUpdatedButtonMask.isEmpty()) {
+        auto bit = mUpdatedButtonMask.clearFirstMarkedBit();
+        getInputReport()->setBoolUsage(INPUT_COLLECTION_ID_MOUSE, codeMap[bit].usage,
+                mButtonValues.hasBit(bit), 0);
+    }
+
+    // Process motion and scroll changes.
+    if (mRelX != 0) {
+        getInputReport()->setIntUsage(INPUT_COLLECTION_ID_MOUSE, INPUT_USAGE_AXIS_X, mRelX, 0);
+    }
+    if (mRelY != 0) {
+        getInputReport()->setIntUsage(INPUT_COLLECTION_ID_MOUSE, INPUT_USAGE_AXIS_Y, mRelY, 0);
+    }
+    if (mRelWheel != 0) {
+        getInputReport()->setIntUsage(INPUT_COLLECTION_ID_MOUSE, INPUT_USAGE_AXIS_VSCROLL,
+                mRelWheel, 0);
+    }
+    if (mRelHWheel != 0) {
+        getInputReport()->setIntUsage(INPUT_COLLECTION_ID_MOUSE, INPUT_USAGE_AXIS_HSCROLL,
+                mRelHWheel, 0);
+    }
+
+    // Report and reset.
+    getInputReport()->reportEvent(getDeviceHandle());
+    mUpdatedButtonMask.clear();
+    mButtonValues.clear();
+    mRelX = 0;
+    mRelY = 0;
+    mRelWheel = 0;
+    mRelHWheel = 0;
+}
+
+}  // namespace android
diff --git a/modules/input/evdev/MouseInputMapper.h b/modules/input/evdev/MouseInputMapper.h
new file mode 100644
index 0000000..1f8bc06
--- /dev/null
+++ b/modules/input/evdev/MouseInputMapper.h
@@ -0,0 +1,56 @@
+/*
+ * 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_MOUSE_INPUT_MAPPER_H_
+#define ANDROID_MOUSE_INPUT_MAPPER_H_
+
+#include <cstdint>
+
+#include <utils/BitSet.h>
+#include <utils/Timers.h>
+
+#include "InputMapper.h"
+
+namespace android {
+
+class MouseInputMapper : public InputMapper {
+public:
+    virtual ~MouseInputMapper() = default;
+
+    virtual bool configureInputReport(InputDeviceNode* devNode,
+            InputReportDefinition* report) override;
+    virtual void process(const InputEvent& event) override;
+
+private:
+    void processMotion(int32_t code, int32_t value);
+    void processButton(int32_t code, int32_t value);
+    void sync(nsecs_t when);
+
+    BitSet32 mButtonValues;
+    BitSet32 mUpdatedButtonMask;
+
+    int32_t mRelX = 0;
+    int32_t mRelY = 0;
+
+    bool mHaveRelWheel = false;
+    bool mHaveRelHWheel = false;
+    int32_t mRelWheel = 0;
+    int32_t mRelHWheel = 0;
+};
+
+}  // namespace android
+
+#endif  // ANDROID_MOUSE_INPUT_MAPPER_H_
diff --git a/modules/input/evdev/SwitchInputMapper.cpp b/modules/input/evdev/SwitchInputMapper.cpp
new file mode 100644
index 0000000..adc2f63
--- /dev/null
+++ b/modules/input/evdev/SwitchInputMapper.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SwitchInputMapper"
+//#define LOG_NDEBUG 0
+
+#include "SwitchInputMapper.h"
+
+#include <linux/input.h>
+#include <hardware/input.h>
+#include <utils/Log.h>
+
+#include "InputHost.h"
+#include "InputHub.h"
+
+namespace android {
+
+static struct {
+    int32_t scancode;
+    InputUsage usage;
+} codeMap[] = {
+    {SW_LID, INPUT_USAGE_SWITCH_LID},
+    {SW_TABLET_MODE, INPUT_USAGE_SWITCH_UNKNOWN},
+    {SW_HEADPHONE_INSERT, INPUT_USAGE_SWITCH_HEADPHONE_INSERT},
+    {SW_RFKILL_ALL, INPUT_USAGE_SWITCH_UNKNOWN},
+    {SW_MICROPHONE_INSERT, INPUT_USAGE_SWITCH_MICROPHONE_INSERT},
+    {SW_DOCK, INPUT_USAGE_SWITCH_UNKNOWN},
+    {SW_LINEOUT_INSERT, INPUT_USAGE_SWITCH_LINEOUT_INSERT},
+    {SW_JACK_PHYSICAL_INSERT, INPUT_USAGE_SWITCH_UNKNOWN},
+    {SW_VIDEOOUT_INSERT, INPUT_USAGE_SWITCH_UNKNOWN},
+    {SW_CAMERA_LENS_COVER, INPUT_USAGE_SWITCH_CAMERA_LENS_COVER},
+    {SW_KEYPAD_SLIDE, INPUT_USAGE_SWITCH_KEYPAD_SLIDE},
+    {SW_FRONT_PROXIMITY, INPUT_USAGE_SWITCH_UNKNOWN},
+    {SW_ROTATE_LOCK, INPUT_USAGE_SWITCH_UNKNOWN},
+    {SW_LINEIN_INSERT, INPUT_USAGE_SWITCH_UNKNOWN},
+    {0x0e /* unused */, INPUT_USAGE_SWITCH_UNKNOWN},
+    {SW_MAX, INPUT_USAGE_SWITCH_UNKNOWN},
+};
+
+SwitchInputMapper::SwitchInputMapper()
+    : InputMapper() {
+    static_assert(SW_CNT <= 32, "More than 32 switches defined in linux/input.h");
+}
+
+bool SwitchInputMapper::configureInputReport(InputDeviceNode* devNode,
+        InputReportDefinition* report) {
+    InputUsage usages[SW_CNT];
+    int numUsages = 0;
+    for (int32_t i = 0; i < SW_CNT; ++i) {
+        if (devNode->hasSwitch(codeMap[i].scancode)) {
+            usages[numUsages++] = codeMap[i].usage;
+        }
+    }
+    if (numUsages == 0) {
+        ALOGE("SwitchInputMapper found no switches for %s!", devNode->getPath().c_str());
+        return false;
+    }
+    setInputReportDefinition(report);
+    getInputReportDefinition()->addCollection(INPUT_COLLECTION_ID_SWITCH, 1);
+    getInputReportDefinition()->declareUsages(INPUT_COLLECTION_ID_SWITCH, usages, numUsages);
+    return true;
+}
+
+void SwitchInputMapper::process(const InputEvent& event) {
+    ALOGD("processing switch event. type=%d code=%d value=%d",
+            event.type, event.code, event.value);
+    switch (event.type) {
+        case EV_SW:
+            processSwitch(event.code, event.value);
+            break;
+        case EV_SYN:
+            if (event.code == SYN_REPORT) {
+                sync(event.when);
+            }
+            break;
+        default:
+            ALOGD("unknown switch event type: %d", event.type);
+    }
+}
+
+void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) {
+    if (switchCode >= 0 && switchCode < SW_CNT) {
+        if (switchValue) {
+            mSwitchValues.markBit(switchCode);
+        } else {
+            mSwitchValues.clearBit(switchCode);
+        }
+        mUpdatedSwitchMask.markBit(switchCode);
+    }
+}
+
+void SwitchInputMapper::sync(nsecs_t when) {
+    if (mUpdatedSwitchMask.isEmpty()) {
+        // Clear the values just in case.
+        mSwitchValues.clear();
+        return;
+    }
+
+    while (!mUpdatedSwitchMask.isEmpty()) {
+        auto bit = mUpdatedSwitchMask.firstMarkedBit();
+        getInputReport()->setBoolUsage(INPUT_COLLECTION_ID_SWITCH, codeMap[bit].usage,
+                mSwitchValues.hasBit(bit), 0);
+        mUpdatedSwitchMask.clearBit(bit);
+    }
+    getInputReport()->reportEvent(getDeviceHandle());
+    mUpdatedSwitchMask.clear();
+    mSwitchValues.clear();
+}
+
+}  // namespace android
diff --git a/modules/input/evdev/SwitchInputMapper.h b/modules/input/evdev/SwitchInputMapper.h
new file mode 100644
index 0000000..e25c3a5
--- /dev/null
+++ b/modules/input/evdev/SwitchInputMapper.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SWITCH_INPUT_MAPPER_H_
+#define ANDROID_SWITCH_INPUT_MAPPER_H_
+
+#include <cstdint>
+
+#include <utils/BitSet.h>
+#include <utils/Timers.h>
+
+#include "InputMapper.h"
+
+namespace android {
+
+class SwitchInputMapper : public InputMapper {
+public:
+    SwitchInputMapper();
+    virtual ~SwitchInputMapper() = default;
+
+    virtual bool configureInputReport(InputDeviceNode* devNode,
+            InputReportDefinition* report) override;
+    virtual void process(const InputEvent& event) override;
+
+private:
+    void processSwitch(int32_t switchCode, int32_t switchValue);
+    void sync(nsecs_t when);
+
+    BitSet32 mSwitchValues;
+    BitSet32 mUpdatedSwitchMask;
+};
+
+}  // namespace android
+
+#endif  // ANDROID_SWITCH_INPUT_MAPPER_H_
diff --git a/tests/input/evdev/Android.mk b/tests/input/evdev/Android.mk
index 167cbc2..557acba 100644
--- a/tests/input/evdev/Android.mk
+++ b/tests/input/evdev/Android.mk
@@ -2,12 +2,19 @@
 include $(CLEAR_VARS)
 
 LOCAL_C_INCLUDES += hardware/libhardware/modules/input/evdev
+LOCAL_C_INCLUDES += $(TOP)/external/gmock/include
 
 LOCAL_SRC_FILES:= \
+    BitUtils_test.cpp \
     InputDevice_test.cpp \
     InputHub_test.cpp \
+    InputMocks.cpp \
+    MouseInputMapper_test.cpp \
+    SwitchInputMapper_test.cpp \
     TestHelpers.cpp
 
+LOCAL_STATIC_LIBRARIES := libgmock
+
 LOCAL_SHARED_LIBRARIES := \
     libinput_evdev \
     liblog \
diff --git a/tests/input/evdev/BitUtils_test.cpp b/tests/input/evdev/BitUtils_test.cpp
new file mode 100644
index 0000000..76fc8af
--- /dev/null
+++ b/tests/input/evdev/BitUtils_test.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BitUtils.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace tests {
+
+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/InputDevice_test.cpp b/tests/input/evdev/InputDevice_test.cpp
index a96d664..bd57491 100644
--- a/tests/input/evdev/InputDevice_test.cpp
+++ b/tests/input/evdev/InputDevice_test.cpp
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "InputHub_test"
-//#define LOG_NDEBUG 0
+#include "InputDevice.h"
+
+#include <memory>
 
 #include <linux/input.h>
 
@@ -23,8 +24,9 @@
 
 #include <utils/Timers.h>
 
-#include "InputDevice.h"
 #include "InputHub.h"
+#include "InputMocks.h"
+#include "MockInputHost.h"
 
 // # of milliseconds to allow for timing measurements
 #define TIMING_TOLERANCE_MS 25
@@ -32,45 +34,41 @@
 #define MSC_ANDROID_TIME_SEC  0x6
 #define MSC_ANDROID_TIME_USEC 0x7
 
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::ReturnNull;
+
 namespace android {
 namespace tests {
 
-class MockInputDeviceNode : public InputDeviceNode {
-    virtual const std::string& getPath() const override { return mPath; }
+class EvdevDeviceTest : public ::testing::Test {
+protected:
+    virtual void SetUp() {
+        // Creating device identifiers and definitions should always happen.
+        EXPECT_CALL(mHost, createDeviceIdentifier(_, _, _, _, _))
+            .WillOnce(ReturnNull());
+        EXPECT_CALL(mHost, createDeviceDefinition())
+            .WillOnce(Return(&mDeviceDef));
+        // InputMappers may cause any of these to be called, but we are not
+        // testing these here.
+        ON_CALL(mHost, createInputReportDefinition())
+            .WillByDefault(Return(&mReportDef));
+        ON_CALL(mHost, createOutputReportDefinition())
+            .WillByDefault(Return(&mReportDef));
+        ON_CALL(mHost, registerDevice(_, _))
+            .WillByDefault(ReturnNull());
+    }
 
-    virtual 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) {}
-
-    virtual void disableDriverKeyRepeat() {}
-
-private:
-    std::string mPath = "/test";
-    std::string mName = "Test Device";
-    std::string mLocation = "test/0";
-    std::string mUniqueId = "test-id";
+    MockInputHost mHost;
+    // Ignore uninteresting calls on the report definitions by using NiceMocks.
+    NiceMock<MockInputReportDefinition> mReportDef;
+    NiceMock<MockInputDeviceDefinition> mDeviceDef;
 };
 
-TEST(EvdevDeviceTest, testOverrideTime) {
+TEST_F(EvdevDeviceTest, testOverrideTime) {
     auto node = std::make_shared<MockInputDeviceNode>();
-    auto device = std::make_unique<EvdevDevice>(node);
+    auto device = std::make_unique<EvdevDevice>(&mHost, node);
     ASSERT_TRUE(device != nullptr);
 
     // Send two timestamp override events before an input event.
@@ -97,9 +95,9 @@
     EXPECT_EQ(when, keyUp.when);
 }
 
-TEST(EvdevDeviceTest, testWrongClockCorrection) {
+TEST_F(EvdevDeviceTest, testWrongClockCorrection) {
     auto node = std::make_shared<MockInputDeviceNode>();
-    auto device = std::make_unique<EvdevDevice>(node);
+    auto device = std::make_unique<EvdevDevice>(&mHost, node);
     ASSERT_TRUE(device != nullptr);
 
     auto now = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -113,9 +111,9 @@
     EXPECT_NEAR(now, event.when, ms2ns(TIMING_TOLERANCE_MS));
 }
 
-TEST(EvdevDeviceTest, testClockCorrectionOk) {
+TEST_F(EvdevDeviceTest, testClockCorrectionOk) {
     auto node = std::make_shared<MockInputDeviceNode>();
-    auto device = std::make_unique<EvdevDevice>(node);
+    auto device = std::make_unique<EvdevDevice>(&mHost, node);
     ASSERT_TRUE(device != nullptr);
 
     auto now = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -130,5 +128,73 @@
     EXPECT_NEAR(now, event.when, ms2ns(TIMING_TOLERANCE_MS));
 }
 
+TEST_F(EvdevDeviceTest, testN7v2Touchscreen) {
+    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getElanTouchscreen());
+    auto device = std::make_unique<EvdevDevice>(&mHost, node);
+    EXPECT_EQ(INPUT_DEVICE_CLASS_TOUCH|INPUT_DEVICE_CLASS_TOUCH_MT,
+            device->getInputClasses());
+}
+
+TEST_F(EvdevDeviceTest, testN7v2ButtonJack) {
+    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getButtonJack());
+    auto device = std::make_unique<EvdevDevice>(&mHost, node);
+    EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
+}
+
+TEST_F(EvdevDeviceTest, testN7v2HeadsetJack) {
+    // Eventually these mock device tests will all expect these calls. For now
+    // only the SwitchInputMapper has been implemented.
+    // TODO: move this expectation out to a common function
+    EXPECT_CALL(mHost, createInputReportDefinition());
+    EXPECT_CALL(mHost, createOutputReportDefinition());
+    EXPECT_CALL(mHost, freeReportDefinition(_));
+    EXPECT_CALL(mHost, registerDevice(_, _));
+
+    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getHeadsetJack());
+    auto device = std::make_unique<EvdevDevice>(&mHost, node);
+    EXPECT_EQ(INPUT_DEVICE_CLASS_SWITCH, device->getInputClasses());
+}
+
+TEST_F(EvdevDeviceTest, testN7v2H2wButton) {
+    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getH2wButton());
+    auto device = std::make_unique<EvdevDevice>(&mHost, node);
+    EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
+}
+
+TEST_F(EvdevDeviceTest, testN7v2GpioKeys) {
+    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexus7v2::getGpioKeys());
+    auto device = std::make_unique<EvdevDevice>(&mHost, node);
+    EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
+}
+
+TEST_F(EvdevDeviceTest, testNexusPlayerGpioKeys) {
+    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexusPlayer::getGpioKeys());
+    auto device = std::make_unique<EvdevDevice>(&mHost, node);
+    EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
+}
+
+TEST_F(EvdevDeviceTest, testNexusPlayerMidPowerBtn) {
+    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexusPlayer::getMidPowerBtn());
+    auto device = std::make_unique<EvdevDevice>(&mHost, node);
+    EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
+}
+
+TEST_F(EvdevDeviceTest, testNexusRemote) {
+    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexusPlayer::getNexusRemote());
+    auto device = std::make_unique<EvdevDevice>(&mHost, node);
+    EXPECT_EQ(INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
+}
+
+TEST_F(EvdevDeviceTest, testAsusGamepad) {
+    auto node = std::shared_ptr<MockInputDeviceNode>(MockNexusPlayer::getAsusGamepad());
+    auto device = std::make_unique<EvdevDevice>(&mHost, node);
+    EXPECT_EQ(INPUT_DEVICE_CLASS_JOYSTICK|INPUT_DEVICE_CLASS_KEYBOARD, device->getInputClasses());
+}
+
+TEST_F(EvdevDeviceTest, testMocks) {
+    auto node = std::make_shared<MockInputDeviceNode>();
+    auto device = std::make_unique<EvdevDevice>(&mHost, node);
+}
+
 }  // namespace tests
 }  // namespace android
diff --git a/tests/input/evdev/InputHub_test.cpp b/tests/input/evdev/InputHub_test.cpp
index f2c8edf..64671ff 100644
--- a/tests/input/evdev/InputHub_test.cpp
+++ b/tests/input/evdev/InputHub_test.cpp
@@ -14,22 +14,19 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "InputHub_test"
-//#define LOG_NDEBUG 0
-
-#include <linux/input.h>
+#include "InputHub.h"
 
 #include <chrono>
 #include <memory>
 #include <mutex>
 
+#include <linux/input.h>
+
 #include <gtest/gtest.h>
 
-#include <utils/Log.h>
 #include <utils/StopWatch.h>
 #include <utils/Timers.h>
 
-#include "InputHub.h"
 #include "TestHelpers.h"
 
 // # of milliseconds to fudge stopwatch measurements
@@ -41,11 +38,11 @@
 
 using namespace std::literals::chrono_literals;
 
-using InputCbFunc = std::function<void(std::shared_ptr<InputDeviceNode>, InputEvent&, nsecs_t)>;
-using DeviceCbFunc = std::function<void(std::shared_ptr<InputDeviceNode>)>;
+using InputCbFunc = std::function<void(const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t)>;
+using DeviceCbFunc = std::function<void(const std::shared_ptr<InputDeviceNode>&)>;
 
-static const InputCbFunc kNoopInputCb = [](std::shared_ptr<InputDeviceNode>, InputEvent&, nsecs_t){};
-static const DeviceCbFunc kNoopDeviceCb = [](std::shared_ptr<InputDeviceNode>){};
+static const InputCbFunc kNoopInputCb = [](const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t){};
+static const DeviceCbFunc kNoopDeviceCb = [](const std::shared_ptr<InputDeviceNode>&){};
 
 class TestInputCallback : public InputCallbackInterface {
 public:
@@ -57,14 +54,14 @@
     void setDeviceAddedCallback(DeviceCbFunc cb) { mDeviceAddedCb = cb; }
     void setDeviceRemovedCallback(DeviceCbFunc cb) { mDeviceRemovedCb = cb; }
 
-    virtual void onInputEvent(std::shared_ptr<InputDeviceNode> node, InputEvent& event,
+    virtual void onInputEvent(const std::shared_ptr<InputDeviceNode>& node, InputEvent& event,
             nsecs_t event_time) override {
         mInputCb(node, event, event_time);
     }
-    virtual void onDeviceAdded(std::shared_ptr<InputDeviceNode> node) override {
+    virtual void onDeviceAdded(const std::shared_ptr<InputDeviceNode>& node) override {
         mDeviceAddedCb(node);
     }
-    virtual void onDeviceRemoved(std::shared_ptr<InputDeviceNode> node) override {
+    virtual void onDeviceRemoved(const std::shared_ptr<InputDeviceNode>& node) override {
         mDeviceRemovedCb(node);
     }
 
@@ -101,7 +98,7 @@
     std::string pathname;
     // Expect that this callback will run and set handle and pathname.
     mCallback->setDeviceAddedCallback(
-            [&](std::shared_ptr<InputDeviceNode> node) {
+            [&](const std::shared_ptr<InputDeviceNode>& node) {
                 pathname = node->getPath();
             });
 
@@ -136,11 +133,11 @@
     std::shared_ptr<InputDeviceNode> tempNode;
     // Expect that these callbacks will run for the above device file.
     mCallback->setDeviceAddedCallback(
-            [&](std::shared_ptr<InputDeviceNode> node) {
+            [&](const std::shared_ptr<InputDeviceNode>& node) {
                 tempNode = node;
             });
     mCallback->setDeviceRemovedCallback(
-            [&](std::shared_ptr<InputDeviceNode> node) {
+            [&](const std::shared_ptr<InputDeviceNode>& node) {
                 EXPECT_EQ(tempNode, node);
             });
 
@@ -182,7 +179,8 @@
     // Expect this callback to run when the input event is read.
     nsecs_t expectedWhen = systemTime(CLOCK_MONOTONIC) + ms2ns(inputDelayMs.count());
     mCallback->setInputCallback(
-            [&](std::shared_ptr<InputDeviceNode> node, InputEvent& event, nsecs_t event_time) {
+            [&](const std::shared_ptr<InputDeviceNode>& node, InputEvent& event,
+                nsecs_t event_time) {
                 EXPECT_NEAR(expectedWhen, event_time, ms2ns(TIMING_TOLERANCE_MS));
                 EXPECT_EQ(s2ns(1), event.when);
                 EXPECT_EQ(tempFileName, node->getPath());
@@ -211,7 +209,7 @@
     // Setup the callback for input events. Should run before the device
     // callback.
     mCallback->setInputCallback(
-            [&](std::shared_ptr<InputDeviceNode>, InputEvent&, nsecs_t) {
+            [&](const std::shared_ptr<InputDeviceNode>&, InputEvent&, nsecs_t) {
                 ASSERT_FALSE(deviceCallbackFinished);
                 inputCallbackFinished = true;
             });
@@ -219,7 +217,7 @@
     // Setup the callback for device removal. Should run after the input
     // callback.
     mCallback->setDeviceRemovedCallback(
-            [&](std::shared_ptr<InputDeviceNode> node) {
+            [&](const std::shared_ptr<InputDeviceNode>& node) {
                 ASSERT_TRUE(inputCallbackFinished)
                     << "input callback did not run before device changed callback";
                 // Make sure the correct device was removed.
diff --git a/tests/input/evdev/InputMocks.cpp b/tests/input/evdev/InputMocks.cpp
new file mode 100644
index 0000000..bd09a3d
--- /dev/null
+++ b/tests/input/evdev/InputMocks.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "InputMocks.h"
+
+namespace android {
+
+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
+
+}  // namespace android
diff --git a/tests/input/evdev/InputMocks.h b/tests/input/evdev/InputMocks.h
new file mode 100644
index 0000000..78e0279
--- /dev/null
+++ b/tests/input/evdev/InputMocks.h
@@ -0,0 +1,142 @@
+/*
+ * 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 <set>
+#include <string>
+
+#include <linux/input.h>
+
+#include "InputHub.h"
+
+namespace android {
+
+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
+
+}  // namespace android
+
+#endif  // ANDROID_INPUT_MOCKS_H_
diff --git a/tests/input/evdev/MockInputHost.h b/tests/input/evdev/MockInputHost.h
new file mode 100644
index 0000000..aae0564
--- /dev/null
+++ b/tests/input/evdev/MockInputHost.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MOCK_INPUT_HOST_H_
+#define ANDROID_MOCK_INPUT_HOST_H_
+
+#include "InputHost.h"
+
+#include "gmock/gmock.h"
+
+
+namespace android {
+namespace tests {
+
+class MockInputReport : public InputReport {
+public:
+    MockInputReport() : InputReport(nullptr, {}, nullptr) {}
+    MOCK_METHOD4(setIntUsage, void(InputCollectionId id, InputUsage usage, int32_t value,
+                int32_t arityIndex));
+    MOCK_METHOD4(setBoolUsage, void(InputCollectionId id, InputUsage usage, bool value,
+                int32_t arityIndex));
+    MOCK_METHOD1(reportEvent, void(InputDeviceHandle* d));
+};
+
+class MockInputReportDefinition : public InputReportDefinition {
+public:
+    MockInputReportDefinition() : InputReportDefinition(nullptr, {}, nullptr) {}
+    MOCK_METHOD2(addCollection, void(InputCollectionId id, int32_t arity));
+    MOCK_METHOD5(declareUsage, void(InputCollectionId id, InputUsage usage, int32_t min,
+                int32_t max, float resolution));
+    MOCK_METHOD3(declareUsages, void(InputCollectionId id, InputUsage* usage, size_t usageCount));
+    MOCK_METHOD0(allocateReport, InputReport*());
+};
+
+class MockInputDeviceDefinition : public InputDeviceDefinition {
+public:
+    MockInputDeviceDefinition() : InputDeviceDefinition(nullptr, {}, nullptr) {}
+    MOCK_METHOD1(addReport, void(InputReportDefinition* r));
+};
+
+class MockInputProperty : public InputProperty {
+public:
+    MockInputProperty() : InputProperty(nullptr, {}, nullptr) {}
+    virtual ~MockInputProperty() {}
+    MOCK_CONST_METHOD0(getKey, const char*());
+    MOCK_CONST_METHOD0(getValue, const char*());
+};
+
+class MockInputPropertyMap : public InputPropertyMap {
+public:
+    MockInputPropertyMap() : InputPropertyMap(nullptr, {}, nullptr) {}
+    virtual ~MockInputPropertyMap() {}
+    MOCK_CONST_METHOD1(getDeviceProperty, InputProperty*(const char* key));
+    MOCK_CONST_METHOD1(freeDeviceProperty, void(InputProperty* property));
+};
+
+class MockInputHost : public InputHostInterface {
+public:
+    MOCK_METHOD5(createDeviceIdentifier, InputDeviceIdentifier*(
+                const char* name, int32_t productId, int32_t vendorId, InputBus bus,
+                const char* uniqueId));
+    MOCK_METHOD0(createDeviceDefinition, InputDeviceDefinition*());
+    MOCK_METHOD0(createInputReportDefinition, InputReportDefinition*());
+    MOCK_METHOD0(createOutputReportDefinition, InputReportDefinition*());
+    MOCK_METHOD1(freeReportDefinition, void(InputReportDefinition* reportDef));
+    MOCK_METHOD2(registerDevice, InputDeviceHandle*(InputDeviceIdentifier* id,
+                InputDeviceDefinition* d));
+    MOCK_METHOD1(unregisterDevice, void(InputDeviceHandle* handle));
+    MOCK_METHOD1(getDevicePropertyMap, InputPropertyMap*(InputDeviceIdentifier* id));
+    MOCK_METHOD1(freeDevicePropertyMap, void(InputPropertyMap* propertyMap));
+};
+
+}  // namespace tests
+}  // namespace android
+
+#endif  // ANDROID_MOCK_INPUT_HOST_H_
diff --git a/tests/input/evdev/MouseInputMapper_test.cpp b/tests/input/evdev/MouseInputMapper_test.cpp
new file mode 100644
index 0000000..7e1f376
--- /dev/null
+++ b/tests/input/evdev/MouseInputMapper_test.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+
+#include <linux/input.h>
+
+#include <gtest/gtest.h>
+
+#include "InputMocks.h"
+#include "MockInputHost.h"
+#include "MouseInputMapper.h"
+
+using ::testing::_;
+using ::testing::Args;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::UnorderedElementsAre;
+
+namespace android {
+namespace tests {
+
+class MouseInputMapperTest : public ::testing::Test {
+protected:
+     virtual void SetUp() override {
+         mMapper = std::make_unique<MouseInputMapper>();
+     }
+
+     MockInputHost mHost;
+     std::unique_ptr<MouseInputMapper> mMapper;
+};
+
+TEST_F(MouseInputMapperTest, testConfigureDevice) {
+    MockInputReportDefinition reportDef;
+    MockInputDeviceNode deviceNode;
+    deviceNode.addKeys(BTN_LEFT, BTN_RIGHT, BTN_MIDDLE);
+    deviceNode.addRelAxis(REL_X);
+    deviceNode.addRelAxis(REL_Y);
+
+    const auto id = INPUT_COLLECTION_ID_MOUSE;
+    EXPECT_CALL(reportDef, addCollection(id, 1));
+    EXPECT_CALL(reportDef, declareUsage(id, INPUT_USAGE_AXIS_X, _, _, _));
+    EXPECT_CALL(reportDef, declareUsage(id, INPUT_USAGE_AXIS_Y, _, _, _));
+    EXPECT_CALL(reportDef, declareUsages(id, _, 3))
+        .With(Args<1,2>(UnorderedElementsAre(
+                        INPUT_USAGE_BUTTON_PRIMARY,
+                        INPUT_USAGE_BUTTON_SECONDARY,
+                        INPUT_USAGE_BUTTON_TERTIARY)));
+
+    EXPECT_TRUE(mMapper->configureInputReport(&deviceNode, &reportDef));
+}
+
+TEST_F(MouseInputMapperTest, testConfigureDevice_noXAxis) {
+    MockInputReportDefinition reportDef;
+    MockInputDeviceNode deviceNode;
+
+    EXPECT_CALL(reportDef, addCollection(INPUT_COLLECTION_ID_MOUSE, 1));
+    EXPECT_CALL(reportDef, declareUsage(_, _, _, _, _)).Times(0);
+    EXPECT_CALL(reportDef, declareUsages(_, _, _)).Times(0);
+
+    EXPECT_FALSE(mMapper->configureInputReport(&deviceNode, &reportDef));
+}
+
+TEST_F(MouseInputMapperTest, testProcessInput) {
+    MockInputReportDefinition reportDef;
+    MockInputDeviceNode deviceNode;
+    deviceNode.addKeys(BTN_LEFT, BTN_RIGHT, BTN_MIDDLE);
+    deviceNode.addRelAxis(REL_X);
+    deviceNode.addRelAxis(REL_Y);
+
+    EXPECT_CALL(reportDef, addCollection(_, _));
+    EXPECT_CALL(reportDef, declareUsage(_, _, _, _, _)).Times(2);
+    EXPECT_CALL(reportDef, declareUsages(_, _, 3));
+
+    mMapper->configureInputReport(&deviceNode, &reportDef);
+
+    MockInputReport report;
+    EXPECT_CALL(reportDef, allocateReport())
+        .WillOnce(Return(&report));
+
+    {
+        // Test two switch events in order
+        InSequence s;
+        const auto id = INPUT_COLLECTION_ID_MOUSE;
+        EXPECT_CALL(report, setIntUsage(id, INPUT_USAGE_AXIS_X, 5, 0));
+        EXPECT_CALL(report, setIntUsage(id, INPUT_USAGE_AXIS_Y, -3, 0));
+        EXPECT_CALL(report, reportEvent(_));
+        EXPECT_CALL(report, setBoolUsage(id, INPUT_USAGE_BUTTON_PRIMARY, 1, 0));
+        EXPECT_CALL(report, reportEvent(_));
+        EXPECT_CALL(report, setBoolUsage(id, INPUT_USAGE_BUTTON_PRIMARY, 0, 0));
+        EXPECT_CALL(report, reportEvent(_));
+    }
+
+    InputEvent events[] = {
+        {0, EV_REL, REL_X, 5},
+        {1, EV_REL, REL_Y, -3},
+        {2, EV_SYN, SYN_REPORT, 0},
+        {0, EV_KEY, BTN_LEFT, 1},
+        {1, EV_SYN, SYN_REPORT, 0},
+        {2, EV_KEY, BTN_LEFT, 0},
+        {3, EV_SYN, SYN_REPORT, 0},
+    };
+    for (auto e : events) {
+        mMapper->process(e);
+    }
+}
+
+}  // namespace tests
+}  // namespace android
+
diff --git a/tests/input/evdev/SwitchInputMapper_test.cpp b/tests/input/evdev/SwitchInputMapper_test.cpp
new file mode 100644
index 0000000..ee90b2c
--- /dev/null
+++ b/tests/input/evdev/SwitchInputMapper_test.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+
+#include <linux/input.h>
+
+#include <gtest/gtest.h>
+
+#include "InputMocks.h"
+#include "MockInputHost.h"
+#include "SwitchInputMapper.h"
+
+using ::testing::_;
+using ::testing::Args;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::UnorderedElementsAre;
+
+namespace android {
+namespace tests {
+
+class SwitchInputMapperTest : public ::testing::Test {
+protected:
+     virtual void SetUp() override {
+         mMapper = std::make_unique<SwitchInputMapper>();
+     }
+
+     MockInputHost mHost;
+     std::unique_ptr<SwitchInputMapper> mMapper;
+};
+
+TEST_F(SwitchInputMapperTest, testConfigureDevice) {
+    MockInputReportDefinition reportDef;
+    MockInputDeviceNode deviceNode;
+    deviceNode.addSwitch(SW_LID);
+    deviceNode.addSwitch(SW_CAMERA_LENS_COVER);
+
+    EXPECT_CALL(reportDef, addCollection(INPUT_COLLECTION_ID_SWITCH, 1));
+    EXPECT_CALL(reportDef, declareUsages(INPUT_COLLECTION_ID_SWITCH, _, 2))
+        .With(Args<1,2>(UnorderedElementsAre(INPUT_USAGE_SWITCH_LID,
+                        INPUT_USAGE_SWITCH_CAMERA_LENS_COVER)));
+
+    EXPECT_TRUE(mMapper->configureInputReport(&deviceNode, &reportDef));
+}
+
+TEST_F(SwitchInputMapperTest, testConfigureDevice_noSwitches) {
+    MockInputReportDefinition reportDef;
+    MockInputDeviceNode deviceNode;
+
+    EXPECT_CALL(reportDef, addCollection(_, _)).Times(0);
+    EXPECT_CALL(reportDef, declareUsages(_, _, _)).Times(0);
+
+    EXPECT_FALSE(mMapper->configureInputReport(&deviceNode, &reportDef));
+}
+
+TEST_F(SwitchInputMapperTest, testProcessInput) {
+    MockInputReportDefinition reportDef;
+    MockInputDeviceNode deviceNode;
+    deviceNode.addSwitch(SW_LID);
+
+    EXPECT_CALL(reportDef, addCollection(_, _));
+    EXPECT_CALL(reportDef, declareUsages(_, _, _));
+
+    mMapper->configureInputReport(&deviceNode, &reportDef);
+
+    MockInputReport report;
+    EXPECT_CALL(reportDef, allocateReport())
+        .WillOnce(Return(&report));
+
+    {
+        // Test two switch events in order
+        InSequence s;
+        EXPECT_CALL(report, setBoolUsage(INPUT_COLLECTION_ID_SWITCH, INPUT_USAGE_SWITCH_LID, 1, 0));
+        EXPECT_CALL(report, reportEvent(_));
+        EXPECT_CALL(report, setBoolUsage(INPUT_COLLECTION_ID_SWITCH, INPUT_USAGE_SWITCH_LID, 0, 0));
+        EXPECT_CALL(report, reportEvent(_));
+    }
+
+    InputEvent events[] = {
+        {0, EV_SW, SW_LID, 1},
+        {1, EV_SYN, SYN_REPORT, 0},
+        {2, EV_SW, SW_LID, 0},
+        {3, EV_SYN, SYN_REPORT, 0},
+    };
+    for (auto e : events) {
+        mMapper->process(e);
+    }
+}
+
+}  // namespace tests
+}  // namespace android
+
diff --git a/tests/input/evdev/TestHelpers.cpp b/tests/input/evdev/TestHelpers.cpp
index 63b579e..9898a6f 100644
--- a/tests/input/evdev/TestHelpers.cpp
+++ b/tests/input/evdev/TestHelpers.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "TestHelpers"
 #define LOG_NDEBUG 0
 
+#include "TestHelpers.h"
+
 #include <dirent.h>
 #include <fcntl.h>
 #include <stdlib.h>
@@ -26,8 +28,6 @@
 
 #include <utils/Log.h>
 
-#include "TestHelpers.h"
-
 namespace android {
 
 static const char kTmpDirTemplate[] = "/data/local/tmp/XXXXXX";