Add initial support for cursor-based pointing devices.

Some parts stubbed out but you can plug in a mouse and move
a green cursor around to interact with the UI.

Change-Id: I80d597a7f11d3bd92041890f74b3c77326975e6e
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 8f4bac6..4e9fad0 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -755,14 +755,14 @@
         }
     }
     
-    // See if this is a trackball (or mouse).
+    // See if this is a cursor device such as a trackball or mouse.
     if (test_bit(BTN_MOUSE, key_bitmask)) {
         uint8_t rel_bitmask[sizeof_bit_array(REL_MAX + 1)];
         memset(rel_bitmask, 0, sizeof(rel_bitmask));
         LOGV("Getting relative controllers...");
         if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) {
             if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) {
-                device->classes |= INPUT_DEVICE_CLASS_TRACKBALL;
+                device->classes |= INPUT_DEVICE_CLASS_CURSOR;
             }
         }
     }
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index ed0cb8e..0548e61 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -1443,11 +1443,11 @@
 #if DEBUG_DISPATCH_CYCLE
     LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, "
             "xOffset=%f, yOffset=%f, "
-            "windowType=%d, pointerIds=0x%x, "
+            "pointerIds=0x%x, "
             "resumeWithAppendedMotionSample=%s",
             connection->getInputChannelName(), inputTarget->flags,
             inputTarget->xOffset, inputTarget->yOffset,
-            inputTarget->windowType, inputTarget->pointerIds.value,
+            inputTarget->pointerIds.value,
             toString(resumeWithAppendedMotionSample));
 #endif
 
@@ -1826,8 +1826,8 @@
 void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
         const sp<Connection>& connection) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("channel '%s' ~ abortBrokenDispatchCycle - broken=%s",
-            connection->getInputChannelName(), toString(broken));
+    LOGD("channel '%s' ~ abortBrokenDispatchCycle",
+            connection->getInputChannelName());
 #endif
 
     // Clear the outbound queue.
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 51ed09f..a11a010 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -239,9 +239,9 @@
         device->addMapper(new KeyboardInputMapper(device, keyboardSources, keyboardType));
     }
 
-    // Trackball-like devices.
-    if (classes & INPUT_DEVICE_CLASS_TRACKBALL) {
-        device->addMapper(new TrackballInputMapper(device));
+    // Cursor-like devices.
+    if (classes & INPUT_DEVICE_CLASS_CURSOR) {
+        device->addMapper(new CursorInputMapper(device));
     }
 
     // Touchscreen-like devices.
@@ -914,7 +914,7 @@
     if (policyFlags & POLICY_FLAG_FUNCTION) {
         newMetaState |= AMETA_FUNCTION_ON;
     }
-    getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags,
+    getDispatcher()->notifyKey(when, getDeviceId(), mSources, policyFlags,
             down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
             AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
 }
@@ -983,36 +983,40 @@
 }
 
 
-// --- TrackballInputMapper ---
+// --- CursorInputMapper ---
 
-TrackballInputMapper::TrackballInputMapper(InputDevice* device) :
+CursorInputMapper::CursorInputMapper(InputDevice* device) :
         InputMapper(device) {
-    mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
-    mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
-    mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
-    mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
-
     initializeLocked();
 }
 
-TrackballInputMapper::~TrackballInputMapper() {
+CursorInputMapper::~CursorInputMapper() {
 }
 
-uint32_t TrackballInputMapper::getSources() {
-    return AINPUT_SOURCE_TRACKBALL;
+uint32_t CursorInputMapper::getSources() {
+    return mSources;
 }
 
-void TrackballInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
     InputMapper::populateDeviceInfo(info);
 
-    info->addMotionRange(AINPUT_MOTION_RANGE_X, -1.0f, 1.0f, 0.0f, mXScale);
-    info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale);
+    if (mParameters.mode == Parameters::MODE_POINTER) {
+        float minX, minY, maxX, maxY;
+        if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
+            info->addMotionRange(AINPUT_MOTION_RANGE_X, minX, maxX, 0.0f, 0.0f);
+            info->addMotionRange(AINPUT_MOTION_RANGE_Y, minY, maxY, 0.0f, 0.0f);
+        }
+    } else {
+        info->addMotionRange(AINPUT_MOTION_RANGE_X, -1.0f, 1.0f, 0.0f, mXScale);
+        info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale);
+    }
+    info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE, 0.0f, 1.0f, 0.0f, 0.0f);
 }
 
-void TrackballInputMapper::dump(String8& dump) {
+void CursorInputMapper::dump(String8& dump) {
     { // acquire lock
         AutoMutex _l(mLock);
-        dump.append(INDENT2 "Trackball Input Mapper:\n");
+        dump.append(INDENT2 "Cursor Input Mapper:\n");
         dumpParameters(dump);
         dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
         dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
@@ -1021,37 +1025,79 @@
     } // release lock
 }
 
-void TrackballInputMapper::configure() {
+void CursorInputMapper::configure() {
     InputMapper::configure();
 
     // Configure basic parameters.
     configureParameters();
+
+    // Configure device mode.
+    switch (mParameters.mode) {
+    case Parameters::MODE_POINTER:
+        mSources = AINPUT_SOURCE_MOUSE;
+        mXPrecision = 1.0f;
+        mYPrecision = 1.0f;
+        mXScale = 1.0f;
+        mYScale = 1.0f;
+        mPointerController = getPolicy()->obtainPointerController(getDeviceId());
+        break;
+    case Parameters::MODE_NAVIGATION:
+        mSources = AINPUT_SOURCE_TRACKBALL;
+        mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+        mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
+        mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+        mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
+        break;
+    }
 }
 
-void TrackballInputMapper::configureParameters() {
+void CursorInputMapper::configureParameters() {
+    mParameters.mode = Parameters::MODE_POINTER;
+    String8 cursorModeString;
+    if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) {
+        if (cursorModeString == "navigation") {
+            mParameters.mode = Parameters::MODE_NAVIGATION;
+        } else if (cursorModeString != "pointer" && cursorModeString != "default") {
+            LOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
+        }
+    }
+
     mParameters.orientationAware = false;
-    getDevice()->getConfiguration().tryGetProperty(String8("trackball.orientationAware"),
+    getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"),
             mParameters.orientationAware);
 
-    mParameters.associatedDisplayId = mParameters.orientationAware ? 0 : -1;
+    mParameters.associatedDisplayId = mParameters.mode == Parameters::MODE_POINTER
+            || mParameters.orientationAware ? 0 : -1;
 }
 
-void TrackballInputMapper::dumpParameters(String8& dump) {
+void CursorInputMapper::dumpParameters(String8& dump) {
     dump.append(INDENT3 "Parameters:\n");
     dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n",
             mParameters.associatedDisplayId);
+
+    switch (mParameters.mode) {
+    case Parameters::MODE_POINTER:
+        dump.append(INDENT4 "Mode: pointer\n");
+        break;
+    case Parameters::MODE_NAVIGATION:
+        dump.append(INDENT4 "Mode: navigation\n");
+        break;
+    default:
+        assert(false);
+    }
+
     dump.appendFormat(INDENT4 "OrientationAware: %s\n",
             toString(mParameters.orientationAware));
 }
 
-void TrackballInputMapper::initializeLocked() {
+void CursorInputMapper::initializeLocked() {
     mAccumulator.clear();
 
     mLocked.down = false;
     mLocked.downTime = 0;
 }
 
-void TrackballInputMapper::reset() {
+void CursorInputMapper::reset() {
     for (;;) {
         { // acquire lock
             AutoMutex _l(mLock);
@@ -1062,7 +1108,7 @@
             }
         } // release lock
 
-        // Synthesize trackball button up event on reset.
+        // Synthesize button up event on reset.
         nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC);
         mAccumulator.fields = Accumulator::FIELD_BTN_MOUSE;
         mAccumulator.btnMouse = false;
@@ -1072,7 +1118,7 @@
     InputMapper::reset();
 }
 
-void TrackballInputMapper::process(const RawEvent* rawEvent) {
+void CursorInputMapper::process(const RawEvent* rawEvent) {
     switch (rawEvent->type) {
     case EV_KEY:
         switch (rawEvent->scanCode) {
@@ -1109,7 +1155,7 @@
     }
 }
 
-void TrackballInputMapper::sync(nsecs_t when) {
+void CursorInputMapper::sync(nsecs_t when) {
     uint32_t fields = mAccumulator.fields;
     if (fields == 0) {
         return; // no new state changes, so nothing to do
@@ -1133,8 +1179,8 @@
         }
 
         downTime = mLocked.downTime;
-        float x = fields & Accumulator::FIELD_REL_X ? mAccumulator.relX * mXScale : 0.0f;
-        float y = fields & Accumulator::FIELD_REL_Y ? mAccumulator.relY * mYScale : 0.0f;
+        float deltaX = fields & Accumulator::FIELD_REL_X ? mAccumulator.relX * mXScale : 0.0f;
+        float deltaY = fields & Accumulator::FIELD_REL_Y ? mAccumulator.relY * mYScale : 0.0f;
 
         if (downChanged) {
             motionEventAction = mLocked.down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
@@ -1142,18 +1188,8 @@
             motionEventAction = AMOTION_EVENT_ACTION_MOVE;
         }
 
-        pointerCoords.x = x;
-        pointerCoords.y = y;
-        pointerCoords.pressure = mLocked.down ? 1.0f : 0.0f;
-        pointerCoords.size = 0;
-        pointerCoords.touchMajor = 0;
-        pointerCoords.touchMinor = 0;
-        pointerCoords.toolMajor = 0;
-        pointerCoords.toolMinor = 0;
-        pointerCoords.orientation = 0;
-
         if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0
-                && (x != 0.0f || y != 0.0f)) {
+                && (deltaX != 0.0f || deltaY != 0.0f)) {
             // Rotate motion based on display orientation if needed.
             // Note: getDisplayInfo is non-reentrant so we can continue holding the lock.
             int32_t orientation;
@@ -1165,35 +1201,54 @@
             float temp;
             switch (orientation) {
             case InputReaderPolicyInterface::ROTATION_90:
-                temp = pointerCoords.x;
-                pointerCoords.x = pointerCoords.y;
-                pointerCoords.y = - temp;
+                temp = deltaX;
+                deltaX = deltaY;
+                deltaY = -temp;
                 break;
 
             case InputReaderPolicyInterface::ROTATION_180:
-                pointerCoords.x = - pointerCoords.x;
-                pointerCoords.y = - pointerCoords.y;
+                deltaX = -deltaX;
+                deltaY = -deltaY;
                 break;
 
             case InputReaderPolicyInterface::ROTATION_270:
-                temp = pointerCoords.x;
-                pointerCoords.x = - pointerCoords.y;
-                pointerCoords.y = temp;
+                temp = deltaX;
+                deltaX = -deltaY;
+                deltaY = temp;
                 break;
             }
         }
+
+        if (mPointerController != NULL) {
+            mPointerController->move(deltaX, deltaY);
+            if (downChanged) {
+                mPointerController->setButtonState(mLocked.down ? POINTER_BUTTON_1 : 0);
+            }
+            mPointerController->getPosition(&pointerCoords.x, &pointerCoords.y);
+        } else {
+            pointerCoords.x = deltaX;
+            pointerCoords.y = deltaY;
+        }
+
+        pointerCoords.pressure = mLocked.down ? 1.0f : 0.0f;
+        pointerCoords.size = 0;
+        pointerCoords.touchMajor = 0;
+        pointerCoords.touchMinor = 0;
+        pointerCoords.toolMajor = 0;
+        pointerCoords.toolMinor = 0;
+        pointerCoords.orientation = 0;
     } // release lock
 
     int32_t metaState = mContext->getGlobalMetaState();
     int32_t pointerId = 0;
-    getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TRACKBALL, 0,
+    getDispatcher()->notifyMotion(when, getDeviceId(), mSources, 0,
             motionEventAction, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE,
             1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime);
 
     mAccumulator.clear();
 }
 
-int32_t TrackballInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
+int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
     if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
         return getEventHub()->getScanCodeState(getDeviceId(), scanCode);
     } else {
@@ -1217,15 +1272,7 @@
 }
 
 uint32_t TouchInputMapper::getSources() {
-    switch (mParameters.deviceType) {
-    case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
-        return AINPUT_SOURCE_TOUCHSCREEN;
-    case Parameters::DEVICE_TYPE_TOUCH_PAD:
-        return AINPUT_SOURCE_TOUCHPAD;
-    default:
-        assert(false);
-        return AINPUT_SOURCE_UNKNOWN;
-    }
+    return mSources;
 }
 
 void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
@@ -1326,6 +1373,18 @@
     // Configure basic parameters.
     configureParameters();
 
+    // Configure sources.
+    switch (mParameters.deviceType) {
+    case Parameters::DEVICE_TYPE_TOUCH_SCREEN:
+        mSources = AINPUT_SOURCE_TOUCHSCREEN;
+        break;
+    case Parameters::DEVICE_TYPE_TOUCH_PAD:
+        mSources = AINPUT_SOURCE_TOUCHPAD;
+        break;
+    default:
+        assert(false);
+    }
+
     // Configure absolute axis information.
     configureRawAxes();
 
@@ -2560,7 +2619,7 @@
         yPrecision = mLocked.orientedYPrecision;
     } // release lock
 
-    getDispatcher()->notifyMotion(when, getDeviceId(), getSources(), policyFlags,
+    getDispatcher()->notifyMotion(when, getDeviceId(), mSources, policyFlags,
             motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags,
             pointerCount, pointerIds, pointerCoords,
             xPrecision, yPrecision, mDownTime);
diff --git a/libs/ui/tests/InputReader_test.cpp b/libs/ui/tests/InputReader_test.cpp
index 97cbc25..50d3383 100644
--- a/libs/ui/tests/InputReader_test.cpp
+++ b/libs/ui/tests/InputReader_test.cpp
@@ -30,6 +30,56 @@
 }
 
 
+// --- FakePointerController ---
+
+class FakePointerController : public PointerControllerInterface {
+    bool mHaveBounds;
+    float mMinX, mMinY, mMaxX, mMaxY;
+
+protected:
+    virtual ~FakePointerController() { }
+
+public:
+    FakePointerController() {
+    }
+
+    void setBounds(float minX, float minY, float maxX, float maxY) {
+        mHaveBounds = true;
+        mMinX = minX;
+        mMinY = minY;
+        mMaxX = maxX;
+        mMaxY = maxY;
+    }
+
+private:
+    virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const {
+        *outMinX = mMinX;
+        *outMaxX = mMinX;
+        *outMinY = mMinY;
+        *outMaxY = mMaxY;
+        return mHaveBounds;
+    }
+
+    virtual void move(float deltaX, float deltaY) {
+    }
+
+    virtual void setButtonState(uint32_t buttonState) {
+    }
+
+    virtual uint32_t getButtonState() const {
+        return 0;
+    }
+
+    virtual void setPosition(float x, float y) {
+    }
+
+    virtual void getPosition(float* outX, float* outY) const {
+        *outX = 0;
+        *outY = 0;
+    }
+};
+
+
 // --- FakeInputReaderPolicy ---
 
 class FakeInputReaderPolicy : public InputReaderPolicyInterface {
@@ -43,6 +93,7 @@
     bool mFilterTouchEvents;
     bool mFilterJumpyTouchEvents;
     Vector<String8> mExcludedDeviceNames;
+    KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers;
 
 protected:
     virtual ~FakeInputReaderPolicy() { }
@@ -78,6 +129,10 @@
         mExcludedDeviceNames.push(deviceName);
     }
 
+    void setPointerController(int32_t deviceId, const sp<FakePointerController>& controller) {
+        mPointerControllers.add(deviceId, controller);
+    }
+
 private:
     virtual bool getDisplayInfo(int32_t displayId,
             int32_t* width, int32_t* height, int32_t* orientation) {
@@ -109,6 +164,10 @@
     virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) {
         outExcludedDeviceNames.appendVector(mExcludedDeviceNames);
     }
+
+    virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) {
+        return mPointerControllers.valueFor(deviceId);
+    }
 };
 
 
@@ -381,6 +440,11 @@
         device->configuration.addProperty(key, value);
     }
 
+    void addConfigurationMap(int32_t deviceId, const PropertyMap* configuration) {
+        Device* device = getDevice(deviceId);
+        device->configuration.addAll(configuration);
+    }
+
     void addAxis(int32_t deviceId, int axis,
             int32_t minValue, int32_t maxValue, int flat, int fuzz) {
         Device* device = getDevice(deviceId);
@@ -851,8 +915,12 @@
         mFakeEventHub.clear();
     }
 
-    void addDevice(int32_t deviceId, const String8& name, uint32_t classes) {
+    void addDevice(int32_t deviceId, const String8& name, uint32_t classes,
+            const PropertyMap* configuration) {
         mFakeEventHub->addDevice(deviceId, name, classes);
+        if (configuration) {
+            mFakeEventHub->addConfigurationMap(deviceId, configuration);
+        }
         mFakeEventHub->finishDeviceScan();
         mReader->loopOnce();
         mReader->loopOnce();
@@ -860,12 +928,13 @@
     }
 
     FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId,
-            const String8& name, uint32_t classes, uint32_t sources) {
+            const String8& name, uint32_t classes, uint32_t sources,
+            const PropertyMap* configuration) {
         InputDevice* device = new InputDevice(mReader.get(), deviceId, name);
         FakeInputMapper* mapper = new FakeInputMapper(device, sources);
         device->addMapper(mapper);
         mReader->setNextDevice(device);
-        addDevice(deviceId, name, classes);
+        addDevice(deviceId, name, classes, configuration);
         return mapper;
     }
 };
@@ -881,7 +950,7 @@
 
 TEST_F(InputReaderTest, GetInputConfiguration_WhenAlphabeticKeyboardPresent_ReturnsQwertyKeyboard) {
     ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("keyboard"),
-            INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY));
+            INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY, NULL));
 
     InputConfiguration config;
     mReader->getInputConfiguration(&config);
@@ -893,7 +962,7 @@
 
 TEST_F(InputReaderTest, GetInputConfiguration_WhenTouchScreenPresent_ReturnsFingerTouchScreen) {
     ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("touchscreen"),
-            INPUT_DEVICE_CLASS_TOUCHSCREEN));
+            INPUT_DEVICE_CLASS_TOUCHSCREEN, NULL));
 
     InputConfiguration config;
     mReader->getInputConfiguration(&config);
@@ -903,9 +972,25 @@
     ASSERT_EQ(InputConfiguration::TOUCHSCREEN_FINGER, config.touchScreen);
 }
 
+TEST_F(InputReaderTest, GetInputConfiguration_WhenMousePresent_ReturnsNoNavigation) {
+    PropertyMap configuration;
+    configuration.addProperty(String8("cursor.mode"), String8("pointer"));
+    ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("mouse"),
+            INPUT_DEVICE_CLASS_CURSOR, &configuration));
+
+    InputConfiguration config;
+    mReader->getInputConfiguration(&config);
+
+    ASSERT_EQ(InputConfiguration::KEYBOARD_NOKEYS, config.keyboard);
+    ASSERT_EQ(InputConfiguration::NAVIGATION_NONAV, config.navigation);
+    ASSERT_EQ(InputConfiguration::TOUCHSCREEN_NOTOUCH, config.touchScreen);
+}
+
 TEST_F(InputReaderTest, GetInputConfiguration_WhenTrackballPresent_ReturnsTrackballNavigation) {
+    PropertyMap configuration;
+    configuration.addProperty(String8("cursor.mode"), String8("navigation"));
     ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("trackball"),
-            INPUT_DEVICE_CLASS_TRACKBALL));
+            INPUT_DEVICE_CLASS_CURSOR, &configuration));
 
     InputConfiguration config;
     mReader->getInputConfiguration(&config);
@@ -917,7 +1002,7 @@
 
 TEST_F(InputReaderTest, GetInputConfiguration_WhenDPadPresent_ReturnsDPadNavigation) {
     ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("dpad"),
-            INPUT_DEVICE_CLASS_DPAD));
+            INPUT_DEVICE_CLASS_DPAD, NULL));
 
     InputConfiguration config;
     mReader->getInputConfiguration(&config);
@@ -929,7 +1014,7 @@
 
 TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsValid) {
     ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"),
-            INPUT_DEVICE_CLASS_KEYBOARD));
+            INPUT_DEVICE_CLASS_KEYBOARD, NULL));
 
     InputDeviceInfo info;
     status_t result = mReader->getInputDeviceInfo(1, &info);
@@ -950,7 +1035,7 @@
 }
 
 TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsIgnored) {
-    addDevice(1, String8("ignored"), 0); // no classes so device will be ignored
+    addDevice(1, String8("ignored"), 0, NULL); // no classes so device will be ignored
 
     InputDeviceInfo info;
     status_t result = mReader->getInputDeviceInfo(1, &info);
@@ -960,9 +1045,9 @@
 
 TEST_F(InputReaderTest, GetInputDeviceIds) {
     ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"),
-            INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY));
-    ASSERT_NO_FATAL_FAILURE(addDevice(2, String8("trackball"),
-            INPUT_DEVICE_CLASS_TRACKBALL));
+            INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY, NULL));
+    ASSERT_NO_FATAL_FAILURE(addDevice(2, String8("mouse"),
+            INPUT_DEVICE_CLASS_CURSOR, NULL));
 
     Vector<int32_t> ids;
     mReader->getInputDeviceIds(ids);
@@ -975,7 +1060,7 @@
 TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) {
     FakeInputMapper* mapper = NULL;
     ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"),
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD));
+            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
     mapper->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN);
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(0,
@@ -1002,7 +1087,7 @@
 TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) {
     FakeInputMapper* mapper = NULL;
     ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"),
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD));
+            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
     mapper->setScanCodeState(KEY_A, AKEY_STATE_DOWN);
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(0,
@@ -1029,7 +1114,7 @@
 TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) {
     FakeInputMapper* mapper = NULL;
     ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"),
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD));
+            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
     mapper->setSwitchState(SW_LID, AKEY_STATE_DOWN);
 
     ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(0,
@@ -1056,7 +1141,7 @@
 TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) {
     FakeInputMapper* mapper = NULL;
     ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"),
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD));
+            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
     mapper->addSupportedKeyCode(AKEYCODE_A);
     mapper->addSupportedKeyCode(AKEYCODE_B);
 
@@ -1089,7 +1174,7 @@
 }
 
 TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) {
-    addDevice(1, String8("ignored"), INPUT_DEVICE_CLASS_KEYBOARD);
+    addDevice(1, String8("ignored"), INPUT_DEVICE_CLASS_KEYBOARD, NULL);
 
     FakeInputDispatcher::NotifyConfigurationChangedArgs args;
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyConfigurationChangedWasCalled(&args));
@@ -1099,7 +1184,7 @@
 TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) {
     FakeInputMapper* mapper = NULL;
     ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"),
-            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD));
+            INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD, NULL));
 
     mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, AKEYCODE_A, 1, POLICY_FLAG_WAKE);
     mReader->loopOnce();
@@ -1792,19 +1877,28 @@
 }
 
 
-// --- TrackballInputMapperTest ---
+// --- CursorInputMapperTest ---
 
-class TrackballInputMapperTest : public InputMapperTest {
+class CursorInputMapperTest : public InputMapperTest {
 protected:
     static const int32_t TRACKBALL_MOVEMENT_THRESHOLD;
 
-    void testMotionRotation(TrackballInputMapper* mapper,
+    sp<FakePointerController> mFakePointerController;
+
+    virtual void SetUp() {
+        InputMapperTest::SetUp();
+
+        mFakePointerController = new FakePointerController();
+        mFakePolicy->setPointerController(DEVICE_ID, mFakePointerController);
+    }
+
+    void testMotionRotation(CursorInputMapper* mapper,
             int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY);
 };
 
-const int32_t TrackballInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6;
+const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6;
 
-void TrackballInputMapperTest::testMotionRotation(TrackballInputMapper* mapper,
+void CursorInputMapperTest::testMotionRotation(CursorInputMapper* mapper,
         int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY) {
     FakeInputDispatcher::NotifyMotionArgs args;
 
@@ -1819,15 +1913,53 @@
             0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 }
 
-TEST_F(TrackballInputMapperTest, GetSources) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
+TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addConfigurationProperty("cursor.mode", "pointer");
+    addMapperAndConfigure(mapper);
+
+    ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper->getSources());
+}
+
+TEST_F(CursorInputMapperTest, WhenModeIsNavigation_GetSources_ReturnsTrackball) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
     ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper->getSources());
 }
 
-TEST_F(TrackballInputMapperTest, PopulateDeviceInfo) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
+TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeFromPointerController) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addConfigurationProperty("cursor.mode", "pointer");
+    addMapperAndConfigure(mapper);
+
+    InputDeviceInfo info;
+    mapper->populateDeviceInfo(&info);
+
+    // Initially there may not be a valid motion range.
+    ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_X));
+    ASSERT_EQ(NULL, info.getMotionRange(AINPUT_MOTION_RANGE_Y));
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE,
+            0.0f, 1.0f, 0.0f, 1.0f));
+
+    // When the bounds are set, then there should be a valid motion range.
+    mFakePointerController->setBounds(1, 2, 800, 480);
+
+    InputDeviceInfo info2;
+    mapper->populateDeviceInfo(&info2);
+
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X,
+            1, 800, 0.0f, 1.0f));
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y,
+            2, 480, 0.0f, 1.0f));
+    ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE,
+            0.0f, 1.0f, 0.0f, 1.0f));
+}
+
+TEST_F(CursorInputMapperTest, WhenModeIsNavigation_PopulateDeviceInfo_ReturnsScaledRange) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
     InputDeviceInfo info;
@@ -1839,8 +1971,9 @@
             -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
 }
 
-TEST_F(TrackballInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
+TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
     mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
@@ -1887,8 +2020,9 @@
     ASSERT_EQ(ARBITRARY_TIME, args.downTime);
 }
 
-TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentXYUpdates) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
+TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
     FakeInputDispatcher::NotifyMotionArgs args;
@@ -1911,8 +2045,9 @@
             0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 }
 
-TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
+TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
     FakeInputDispatcher::NotifyMotionArgs args;
@@ -1932,8 +2067,9 @@
             0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 }
 
-TEST_F(TrackballInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
+TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
     FakeInputDispatcher::NotifyMotionArgs args;
@@ -1967,8 +2103,9 @@
             0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 }
 
-TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsNotDown_ShouldNotSynthesizeButtonUp) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
+TEST_F(CursorInputMapperTest, Reset_WhenButtonIsNotDown_ShouldNotSynthesizeButtonUp) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
     FakeInputDispatcher::NotifyMotionArgs args;
@@ -1987,8 +2124,9 @@
     ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled());
 }
 
-TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsDown_ShouldSynthesizeButtonUp) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
+TEST_F(CursorInputMapperTest, Reset_WhenButtonIsDown_ShouldSynthesizeButtonUp) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
     FakeInputDispatcher::NotifyMotionArgs args;
@@ -2006,8 +2144,9 @@
             0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
 }
 
-TEST_F(TrackballInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMotions) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
+TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateMotions) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addConfigurationProperty("cursor.mode", "navigation");
     addMapperAndConfigure(mapper);
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,
@@ -2023,9 +2162,10 @@
     ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1,  1, -1,  1));
 }
 
-TEST_F(TrackballInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) {
-    TrackballInputMapper* mapper = new TrackballInputMapper(mDevice);
-    addConfigurationProperty("trackball.orientationAware", "1");
+TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldRotateMotions) {
+    CursorInputMapper* mapper = new CursorInputMapper(mDevice);
+    addConfigurationProperty("cursor.mode", "navigation");
+    addConfigurationProperty("cursor.orientationAware", "1");
     addMapperAndConfigure(mapper);
 
     mFakePolicy->setDisplayInfo(DISPLAY_ID,