Initial InputMappers for evdev input HAL.

The previous design of the InputHost wrapper classes made it very
painful to do testing, so this change also reverts to a more classical
C++ pattern for non-copyable objects. The InputHost classes still simply
call through to the input_host_t and callbacks as before.

Updated unittests to use gmock for mocking the InputHost interactions.

Change-Id: I4b70df2c89ed48af77446b8f5b87a4bde94510bf
diff --git a/modules/input/evdev/Android.mk b/modules/input/evdev/Android.mk
index d3c49e7..b12867c 100644
--- a/modules/input/evdev/Android.mk
+++ b/modules/input/evdev/Android.mk
@@ -21,7 +21,9 @@
     InputHub.cpp \
     InputDevice.cpp \
     InputDeviceManager.cpp \
-    InputHost.cpp
+    InputHost.cpp \
+    InputMapper.cpp \
+    SwitchInputMapper.cpp
 
 LOCAL_SHARED_LIBRARIES := \
     libhardware_legacy \
diff --git a/modules/input/evdev/EvdevModule.cpp b/modules/input/evdev/EvdevModule.cpp
index 1171a1a..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,7 +46,7 @@
 private:
     void loop();
 
-    InputHost mInputHost;
+    std::unique_ptr<InputHostInterface> mInputHost;
     std::shared_ptr<InputDeviceManager> mDeviceManager;
     std::unique_ptr<InputHub> mInputHub;
     std::thread mPollThread;
@@ -53,9 +54,9 @@
 
 static std::unique_ptr<EvdevModule> gEvdevModule;
 
-EvdevModule::EvdevModule(InputHost inputHost) :
+EvdevModule::EvdevModule(InputHostInterface* inputHost) :
     mInputHost(inputHost),
-    mDeviceManager(std::make_shared<InputDeviceManager>(mInputHost)),
+    mDeviceManager(std::make_shared<InputDeviceManager>(mInputHost.get())),
     mInputHub(std::make_unique<InputHub>(mDeviceManager)) {}
 
 void EvdevModule::init() {
@@ -97,7 +98,7 @@
 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};
+    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 883d6d4..8365386 100644
--- a/modules/input/evdev/InputDevice.cpp
+++ b/modules/input/evdev/InputDevice.cpp
@@ -32,6 +32,8 @@
 
 #include "InputHub.h"
 #include "InputDevice.h"
+#include "InputMapper.h"
+#include "SwitchInputMapper.h"
 
 #define MSC_ANDROID_TIME_SEC  0x6
 #define MSC_ANDROID_TIME_USEC 0x7
@@ -104,49 +106,83 @@
     return value;
 }
 
-static void setDeviceClasses(const InputDeviceNode* node, uint32_t* classes) {
-    // See if this is a keyboard. Ignore everything in the button range except
-    // for joystick and gamepad buttons which are handled like keyboards for the
-    // most part.
-    bool haveKeyboardKeys = node->hasKeyInRange(0, BTN_MISC) ||
-        node->hasKeyInRange(KEY_OK, KEY_CNT);
-    bool haveGamepadButtons = node->hasKeyInRange(BTN_MISC, BTN_MOUSE) ||
-        node->hasKeyInRange(BTN_JOYSTICK, BTN_DIGI);
-    if (haveKeyboardKeys || haveGamepadButtons) {
-        *classes |= INPUT_DEVICE_CLASS_KEYBOARD;
-    }
+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 (node->hasKey(BTN_MOUSE)
-            && node->hasRelativeAxis(REL_X)
-            && node->hasRelativeAxis(REL_Y)) {
-        *classes |= INPUT_DEVICE_CLASS_CURSOR;
+    if (mDeviceNode->hasKey(BTN_MOUSE)
+            && mDeviceNode->hasRelativeAxis(REL_X)
+            && mDeviceNode->hasRelativeAxis(REL_Y)) {
+        mClasses |= INPUT_DEVICE_CLASS_CURSOR;
+        //mMappers.push_back(std::make_unique<CursorInputMapper>());
     }
 
-    // See if this is a touch pad.
+    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 (node->hasAbsoluteAxis(ABS_MT_POSITION_X)
-            && node->hasAbsoluteAxis(ABS_MT_POSITION_Y)) {
+    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 (node->hasKey(BTN_TOUCH) || !haveGamepadButtons) {
-            *classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT;
+        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 (node->hasKey(BTN_TOUCH)
-            && node->hasAbsoluteAxis(ABS_X)
-            && node->hasAbsoluteAxis(ABS_Y)) {
-        *classes != INPUT_DEVICE_CLASS_TOUCH;
+    } 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 ((node->hasAbsoluteAxis(ABS_PRESSURE) || node->hasKey(BTN_TOUCH))
-            && !node->hasAbsoluteAxis(ABS_X) && !node->hasAbsoluteAxis(ABS_Y)) {
-        *classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS;
-        // Keyboard will try to claim some of the buttons but we really want to
-        // reserve those so we can fuse it with the touch screen data, so just
-        // take them back. Note this means an external stylus cannot also be a
-        // keyboard device.
-        *classes &= ~INPUT_DEVICE_CLASS_KEYBOARD;
+    } 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.
@@ -154,11 +190,12 @@
     // distinguish them from other devices such as accelerometers that also have
     // absolute axes.
     if (haveGamepadButtons) {
-        uint32_t assumedClasses = *classes | INPUT_DEVICE_CLASS_JOYSTICK;
+        uint32_t assumedClasses = mClasses | INPUT_DEVICE_CLASS_JOYSTICK;
         for (int i = 0; i < ABS_CNT; ++i) {
-            if (node->hasAbsoluteAxis(i)
+            if (mDeviceNode->hasAbsoluteAxis(i)
                     && getAbsAxisUsage(i, assumedClasses) == INPUT_DEVICE_CLASS_JOYSTICK) {
-                *classes = assumedClasses;
+                mClasses = assumedClasses;
+                //mMappers.push_back(std::make_unique<JoystickInputMapper>());
                 break;
             }
         }
@@ -166,36 +203,40 @@
 
     // Check whether this device has switches.
     for (int i = 0; i < SW_CNT; ++i) {
-        if (node->hasSwitch(i)) {
-            *classes |= INPUT_DEVICE_CLASS_SWITCH;
+        if (mDeviceNode->hasSwitch(i)) {
+            mClasses |= INPUT_DEVICE_CLASS_SWITCH;
+            mMappers.push_back(std::make_unique<SwitchInputMapper>());
             break;
         }
     }
 
     // Check whether this device supports the vibrator.
-    if (node->hasForceFeedback(FF_RUMBLE)) {
-        *classes |= INPUT_DEVICE_CLASS_VIBRATOR;
+    // TODO: decide if this is necessary.
+    if (mDeviceNode->hasForceFeedback(FF_RUMBLE)) {
+        mClasses |= INPUT_DEVICE_CLASS_VIBRATOR;
+        //mMappers.push_back(std::make_unique<VibratorInputMapper>());
     }
 
-    // If the device isn't recognized as something we handle, don't monitor it.
-    // TODO
-
-    ALOGD("device %s classes=0x%x", node->getPath().c_str(), *classes);
+    ALOGD("device %s classes=0x%x %d mappers", mDeviceNode->getPath().c_str(), mClasses,
+            mMappers.size());
 }
 
-EvdevDevice::EvdevDevice(InputHost host, const std::shared_ptr<InputDeviceNode>& node) :
-    mHost(host), mDeviceNode(node) {
+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);
+        }
 
-    InputBus bus = getInputBus(node);
-    mInputId = mHost.createDeviceIdentifier(
-            node->getName().c_str(),
-            node->getProductId(),
-            node->getVendorId(),
-            bus,
-            node->getUniqueId().c_str());
-
-    InputPropertyMap propMap = mHost.getDevicePropertyMap(mInputId);
-    setDeviceClasses(mDeviceNode.get(), &mClasses);
+        reportDef = mHost->createOutputReportDefinition();
+        if (mapper->configureOutputReport(mDeviceNode.get(), reportDef)) {
+            mDeviceDefinition->addReport(reportDef);
+        } else {
+            mHost->freeReportDefinition(reportDef);
+        }
+    }
 }
 
 void EvdevDevice::processInput(InputEvent& event, nsecs_t currentTime) {
@@ -260,6 +301,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 b4f3244..a379d6c 100644
--- a/modules/input/evdev/InputDevice.h
+++ b/modules/input/evdev/InputDevice.h
@@ -18,11 +18,13 @@
 #define ANDROID_INPUT_DEVICE_H_
 
 #include <memory>
+#include <vector>
 
 #include <utils/Timers.h>
 
 #include "InputHost.h"
 #include "InputHub.h"
+#include "InputMapper.h"
 
 namespace android {
 
@@ -45,16 +47,22 @@
  */
 class EvdevDevice : public InputDeviceInterface {
 public:
-    EvdevDevice(InputHost host, const 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:
-    InputHost mHost;
+    void createMappers();
+    void configureDevice();
+
+    InputHostInterface* mHost = nullptr;
     std::shared_ptr<InputDeviceNode> mDeviceNode;
-    InputDeviceIdentifier mInputId;
+    InputDeviceIdentifier* mInputId = nullptr;
+    InputDeviceDefinition* mDeviceDefinition = nullptr;
+    InputDeviceHandle* mDeviceHandle = nullptr;
+    std::vector<std::unique_ptr<InputMapper>> mMappers;
     uint32_t mClasses = 0;
 
     int32_t mOverrideSec = 0;
diff --git a/modules/input/evdev/InputDeviceManager.cpp b/modules/input/evdev/InputDeviceManager.cpp
index f21d6d1..46c9dfb 100644
--- a/modules/input/evdev/InputDeviceManager.cpp
+++ b/modules/input/evdev/InputDeviceManager.cpp
@@ -43,7 +43,8 @@
         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 25dd912..7ffec5b 100644
--- a/modules/input/evdev/InputDeviceManager.h
+++ b/modules/input/evdev/InputDeviceManager.h
@@ -35,7 +35,7 @@
  */
 class InputDeviceManager : public InputCallbackInterface {
 public:
-    explicit InputDeviceManager(InputHost host) :
+    explicit InputDeviceManager(InputHostInterface* host) :
         mHost(host) {}
     virtual ~InputDeviceManager() override = default;
 
@@ -45,7 +45,7 @@
     virtual void onDeviceRemoved(const std::shared_ptr<InputDeviceNode>& node) override;
 
 private:
-    InputHost mHost;
+    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 74a5f8a..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,28 +42,19 @@
             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);
-}
-
-InputProperty::~InputProperty() {
-    mCallbacks.input_free_device_property(mHost, mProperty);
-}
-
-InputProperty::InputProperty(InputProperty&& rhs) :
-    InputHostBase(rhs), mProperty(std::move(rhs.mProperty)) {
-    rhs.mProperty = nullptr;
+void InputDeviceDefinition::addReport(InputReportDefinition* r) {
+    mCallbacks.input_device_definition_add_report(mHost, mDeviceDefinition, *r);
 }
 
 const char* InputProperty::getKey() const {
@@ -64,51 +65,55 @@
     return mCallbacks.input_get_property_value(mHost, mProperty);
 }
 
-InputPropertyMap::~InputPropertyMap() {
-    mCallbacks.input_free_device_property_map(mHost, mMap);
-}
-
-InputPropertyMap::InputPropertyMap(InputPropertyMap&& rhs) :
-    InputHostBase(rhs), mMap(std::move(rhs.mMap)) {
-    rhs.mMap = nullptr;
-}
-
-InputProperty InputPropertyMap::getDeviceProperty(const char* key) const {
-    return InputProperty(mHost, mCallbacks,
+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 d6a04d9..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,8 +43,8 @@
     InputHostBase(input_host_t* host, input_host_callbacks_t cb) : mHost(host), mCallbacks(cb) {}
     virtual ~InputHostBase() = default;
 
-    InputHostBase(const InputHostBase& rhs) = default;
-    InputHostBase(InputHostBase&& rhs) = default;
+    InputHostBase(const InputHostBase& rhs) = delete;
+    InputHostBase(InputHostBase&& rhs) = delete;
 
     input_host_t* mHost;
     input_host_callbacks_t mCallbacks;
@@ -52,140 +52,139 @@
 
 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;
-    const char* getValue() const;
-
-    // Transfers ownership of the input_property_t pointer.
-    InputProperty(InputProperty&& rhs);
-
-    // Prevent copy/assign because of the ownership of the underlying
-    // input_property_t pointer.
     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) const;
-
-    // Transfers ownership of the input_property_map_t pointer.
-    InputPropertyMap(InputPropertyMap&& rhs);
-
-    // Prevent copy/assign because of the ownership of the underlying
-    // input_property_map_t pointer.
     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/InputMapper.cpp b/modules/input/evdev/InputMapper.cpp
new file mode 100644
index 0000000..2353a9a
--- /dev/null
+++ b/modules/input/evdev/InputMapper.cpp
@@ -0,0 +1,28 @@
+/*
+ * 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"
+
+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..b33cb63
--- /dev/null
+++ b/modules/input/evdev/InputMapper.h
@@ -0,0 +1,82 @@
+/*
+ * 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_
+
+#include "InputHost.h"
+#include "InputHub.h"
+
+namespace android {
+
+/**
+ * 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:
+    virtual ~InputMapper() = default;
+
+    /**
+     * 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/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..b3a1c05
--- /dev/null
+++ b/modules/input/evdev/SwitchInputMapper.h
@@ -0,0 +1,52 @@
+/*
+ * 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 InputDeviceNode;
+class InputReportDefinition;
+struct InputEvent;
+
+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_